Mobx
⚠️ 注意:本文档基于 MobX 5/早期 6 的写法(decorate、装饰器、useLocalStore)。MobX 6 推荐使用
makeAutoObservable,不再需要装饰器。以下新增 MobX 6 推荐写法。
Mobx 支持单向数据流,也就是动作改变状态,而状态的改变会更新所有受影响的视图;
Mobx 与 Redux 对比
- mobx 的学习成本比 redux 更少,使用起来比 redux 更方便
- redux 可维护性更强,阅读好,对于大项目建议使用 redux
- mobx 可以说是 redux 的代替品,当使用 redux 比较麻烦时可以选择使用 mobx
Mobx 要点
- 定义数据使用
observable函数 - 更改状态使用
action函数包装,默认不需要,当开启严格模式时需要,建议始终加上
jsx
import React from 'react'
import { render } from 'react-dom'
import { observable, action } from 'mobx'
import App from './App'
// 定义数据
var appState = observable({
timer: 0,
})
// 定义更改状态函数
appState.resetTimer = action(function reset() {
appState.timer = 0
})
setInterval(
action(function tick() {
appState.timer += 1
}),
1000,
)
render(<App appState={appState} />, window.root)import React from 'react'
import { render } from 'react-dom'
import { observable, action } from 'mobx'
import App from './App'
// 定义数据
var appState = observable({
timer: 0,
})
// 定义更改状态函数
appState.resetTimer = action(function reset() {
appState.timer = 0
})
setInterval(
action(function tick() {
appState.timer += 1
}),
1000,
)
render(<App appState={appState} />, window.root)页面监测自动更新 observe
有三种方式:observer、useObserver、Observer
使用 observer
jsx
import React from 'react'
import { observer } from 'mobx-react'
@observer
class App extends React.Component {
render() {
return (
<button
onClick={() => {
this.props.resetTimer()
}}
>
seconds: {this.props.appState.timer}
</button>
)
}
}
export default Appimport React from 'react'
import { observer } from 'mobx-react'
@observer
class App extends React.Component {
render() {
return (
<button
onClick={() => {
this.props.resetTimer()
}}
>
seconds: {this.props.appState.timer}
</button>
)
}
}
export default App使用 useObserver
下面的例子中整个 component 都会被重新渲染
jsx
import { useObserver } from 'mobx-react'
export default (props) => {
return useObserver(() => (
<button onClick={() => props.resetTimer()}>seconds: {props.appState.timer}</button>
))
}import { useObserver } from 'mobx-react'
export default (props) => {
return useObserver(() => (
<button onClick={() => props.resetTimer()}>seconds: {props.appState.timer}</button>
))
}使用 Observer
下面的例子只有 Observer 包裹的地方会被重新渲染
jsx
//使用<Observer></Observer>
import React from 'react'
import { Observer, useLocalStore } from 'mobx-react-lite'
function App() {
const store = useLocalStore(() => ({
age: 18,
setAge: action(() => {
store.age += 1
}),
}))
return (
<div className="App">
<Observer>{() => <h2>当前的isBooks:{store.age}</h2>}</Observer>
<button onClick={store.setAge}>修改值</button>
</div>
)
}//使用<Observer></Observer>
import React from 'react'
import { Observer, useLocalStore } from 'mobx-react-lite'
function App() {
const store = useLocalStore(() => ({
age: 18,
setAge: action(() => {
store.age += 1
}),
}))
return (
<div className="App">
<Observer>{() => <h2>当前的isBooks:{store.age}</h2>}</Observer>
<button onClick={store.setAge}>修改值</button>
</div>
)
}React 项目注入 Mobx 案例
安装插件
yarn add mobx mobx-react mobx-react-lite -S
创建:store/books.js
js
import { observable, action, computed, decorate } from 'mobx'
class BookStore {
list = []
isBook = 'false'
setIsBook = (book) => {
this.isBook = book
}
get book() {
return this.isBook.toString()
}
}
// 定义store, 或者写装饰器模式, 参考官方文档
decorate(BookStore, {
list: observable,
isBook: observable,
setIsBook: action,
book: computed,
})
// 创建并返回store
const store = new BookStore()
export default storeimport { observable, action, computed, decorate } from 'mobx'
class BookStore {
list = []
isBook = 'false'
setIsBook = (book) => {
this.isBook = book
}
get book() {
return this.isBook.toString()
}
}
// 定义store, 或者写装饰器模式, 参考官方文档
decorate(BookStore, {
list: observable,
isBook: observable,
setIsBook: action,
book: computed,
})
// 创建并返回store
const store = new BookStore()
export default store- 创建:store/index.js
js
import { createContext, useContext } from 'react'
import { configure } from 'mobx'
import books from './books'
// 开启严格
configure({ enforceActions: 'observed' })
// 创建store
const createMobxStore = () => ({
books,
})
const store = createMobxStore()
// 创建context上下文
const MobxContext = createContext(store)
const useMobx = () => useContext(MobxContext)
export { useMobx, store, MobxContext }import { createContext, useContext } from 'react'
import { configure } from 'mobx'
import books from './books'
// 开启严格
configure({ enforceActions: 'observed' })
// 创建store
const createMobxStore = () => ({
books,
})
const store = createMobxStore()
// 创建context上下文
const MobxContext = createContext(store)
const useMobx = () => useContext(MobxContext)
export { useMobx, store, MobxContext }- 在入口文件注入
jsx
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'mobx-react'
import { store, MobxContext } from './store'
import App from './App'
ReactDOM.render(
// 用于使用inject注入
<Provider {...store}>
{/* 向下传递上下文, 常用于hooks组件使用 */}
<MobxContext.Provider value={store}>
<App />
</MobxContext.Provider>
</Provider>,
document.getElementById('root'),
)import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'mobx-react'
import { store, MobxContext } from './store'
import App from './App'
ReactDOM.render(
// 用于使用inject注入
<Provider {...store}>
{/* 向下传递上下文, 常用于hooks组件使用 */}
<MobxContext.Provider value={store}>
<App />
</MobxContext.Provider>
</Provider>,
document.getElementById('root'),
)- 在组件中使用
jsx
import React from 'react'
import { inject, observer } from 'mobx-react'
// 函数组件操作方式雷同
class App extends React.Component {
render() {
const { books } = props
return (
<div className="App">
<h2>当前的isBooks:{books.isBook.toString()}</h2>
<button
onClick={() => {
books.setIsBook(!books.isBook)
}}
>
修改值
</button>
</div>
)
}
}
export default inject('books')(observer(App))import React from 'react'
import { inject, observer } from 'mobx-react'
// 函数组件操作方式雷同
class App extends React.Component {
render() {
const { books } = props
return (
<div className="App">
<h2>当前的isBooks:{books.isBook.toString()}</h2>
<button
onClick={() => {
books.setIsBook(!books.isBook)
}}
>
修改值
</button>
</div>
)
}
}
export default inject('books')(observer(App))- hooks 组件使用
jsx
import React from 'react'
import { useMobx } from './store'
import { Observer } from 'mobx-react-lite'
function App() {
const { books } = useMobx()
return (
<div className="App">
<Observer>{() => <h2>当前的isBook状态:{books.isBook.toString()}</h2>}</Observer>
<button onClick={books.setIsBook}>修改值</button>
</div>
)
}
export default Appimport React from 'react'
import { useMobx } from './store'
import { Observer } from 'mobx-react-lite'
function App() {
const { books } = useMobx()
return (
<div className="App">
<Observer>{() => <h2>当前的isBook状态:{books.isBook.toString()}</h2>}</Observer>
<button onClick={books.setIsBook}>修改值</button>
</div>
)
}
export default AppuseLocalStore
函数组件本地使用状态
js
import React from 'react'
import { action } from 'mobx'
import { useLocalStore } from 'mobx-react'
import { Observer } from 'mobx-react-lite'
function App(props) {
const store = useLocalStore(() => ({
age: 18,
setAge: action(() => {
store.age += 1
}),
}))
return (
<div className="App">
<Observer>{() => <h2>当前的isBooks:{store.age}</h2>}</Observer>
<button onClick={store.setAge}>修改值</button>
</div>
)
}import React from 'react'
import { action } from 'mobx'
import { useLocalStore } from 'mobx-react'
import { Observer } from 'mobx-react-lite'
function App(props) {
const store = useLocalStore(() => ({
age: 18,
setAge: action(() => {
store.age += 1
}),
}))
return (
<div className="App">
<Observer>{() => <h2>当前的isBooks:{store.age}</h2>}</Observer>
<button onClick={store.setAge}>修改值</button>
</div>
)
}开启严格模式
开户严格模式之后不允许随地更改,只能通过动作更改
js
import { configure } from 'mobx'
configure({ enforceActions: 'observed' })import { configure } from 'mobx'
configure({ enforceActions: 'observed' })never(默认): 可以在任意地方修改状态observed: 在某处观察到的所有状态都需要通过动作进行更改。在正式应用中推荐此严格模式。always: 状态始终需要通过动作来更新(实际上还包括创建)。
常用 API
官方网站:Mobx 中文文档
observable:监测数据action:动作,用来修改数据runInAction:在 action 里面异步修改数据computed:计算值autorun:追踪 observable 数据变化observer:包裹高阶组件,mobx-react 提供的
异步修改 observable
js
class Demo {
@observable list = []
@action fetchList = async () => {
const res = await fetch('')
runInAction(() => {
this.list = res.results
})
}
}class Demo {
@observable list = []
@action fetchList = async () => {
const res = await fetch('')
runInAction(() => {
this.list = res.results
})
}
}API 定义方式
使用装饰器定义
js
import { observable, action } from 'mobx'
class TaskStore {
@observable tasks = []
@action addTask(task) {
/* ... */
}
}import { observable, action } from 'mobx'
class TaskStore {
@observable tasks = []
@action addTask(task) {
/* ... */
}
}使用第二个参数定义
js
import { observable, action } from 'mobx'
const taskStore = observable(
{
tasks: [],
addTask(task) {},
},
{
tasks: observable.shallow,
addTask: action,
},
)import { observable, action } from 'mobx'
const taskStore = observable(
{
tasks: [],
addTask(task) {},
},
{
tasks: observable.shallow,
addTask: action,
},
)使用 decorate 定义
js
class TodoList {
todos = {}
get unfinishedTodoCount() {}
addTodo() {}
}
decorate(TodoList, {
todos: observable,
unfinishedTodoCount: computed,
addTodo: action,
})class TodoList {
todos = {}
get unfinishedTodoCount() {}
addTodo() {}
}
decorate(TodoList, {
todos: observable,
unfinishedTodoCount: computed,
addTodo: action,
})mobx 日志
js
import { enableLogging } from 'mobx-logger'
// mobx 日志
enableLogging({
predicate: () => true,
action: true,
reaction: true,
transaction: true,
compute: true,
})import { enableLogging } from 'mobx-logger'
// mobx 日志
enableLogging({
predicate: () => true,
action: true,
reaction: true,
transaction: true,
compute: true,
})MobX 6 推荐写法
makeAutoObservable(替代装饰器和 decorate)
js
import { makeAutoObservable } from 'mobx'
class TodoStore {
todos = []
filter = 'all'
constructor() {
makeAutoObservable(this)
}
// 普通方法 → 自动 action
addTodo(title) {
this.todos.push({ title, done: false })
}
// getter → 自动 computed
get unfinishedCount() {
return this.todos.filter((t) => !t.done).length
}
// async
async fetchTodos() {
const res = await fetch('/api/todos')
this.todos = await res.json()
}
}import { makeAutoObservable } from 'mobx'
class TodoStore {
todos = []
filter = 'all'
constructor() {
makeAutoObservable(this)
}
// 普通方法 → 自动 action
addTodo(title) {
this.todos.push({ title, done: false })
}
// getter → 自动 computed
get unfinishedCount() {
return this.todos.filter((t) => !t.done).length
}
// async
async fetchTodos() {
const res = await fetch('/api/todos')
this.todos = await res.json()
}
}useLocalObservable(替代已弃用的 useLocalStore)
jsx
import { useLocalObservable, Observer } from 'mobx-react-lite'
function App() {
const store = useLocalObservable(() => ({
count: 0,
increment() { this.count++ },
}))
return (
<div>
<Observer>{() => <span>{store.count}</span>}</Observer>
<button onClick={store.increment}>+1</button>
</div>
)
}import { useLocalObservable, Observer } from 'mobx-react-lite'
function App() {
const store = useLocalObservable(() => ({
count: 0,
increment() { this.count++ },
}))
return (
<div>
<Observer>{() => <span>{store.count}</span>}</Observer>
<button onClick={store.increment}>+1</button>
</div>
)
}MobX 6 API 变化总结
| 旧写法 | MobX 6 推荐写法 |
|---|---|
@observable 装饰器 | makeAutoObservable(this) 自动推断 |
decorate(Class, { prop: observable }) | makeAutoObservable(this) |
useLocalStore | useLocalObservable 或 useMemo(() => makeAutoObservable({...}), []) |
Provider + inject | React Context 直接传递 |
mobx-react | mobx-react-lite(函数组件推荐) |
当前包版本
bash
npm install mobx mobx-react-lite
# mobx: ^6.12.x
# mobx-react-lite: ^4.xnpm install mobx mobx-react-lite
# mobx: ^6.12.x
# mobx-react-lite: ^4.x
Cao Note