全局状态管理插件设计的核心思想都可以概括成:不能随意的去修改状态(state)。因此我们通常需要一个 action 来统一 modify 我们的 state。
MobX 也是基于这种思想设计的,它具有:
- 定义状态并使其可观察 (observable)
- 创建视图以响应状态的变化(observer、computed)
- 更改状态(action)
Function 组件里使用
由于 React Hooks 的出现,以及注解的不稳定性。在 6.x 中,MobX 升级了它的使用方法。
这里我们以 makeAutoObservable 的 Store 设计为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export class Store6_makeAutoObservable { readonly base = 5; MCount = 0;
constructor() { makeAutoObservable(this); }
setMCount() { this.MCount++; }
get total() { return this.MCount * this.base; } }
export default new Store6;
|
然后在函数组件里,我们需要使用 observer 函数来替代注解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { observer } from "mobx-react"; import store6 from "@/store";
export const MobXFunc: React.FC = observer(() => { return ( <div> <button onClick={() => { store6.setMCount(); }} > count++ </button> <span> Count {store6.MCount} </span> <span> Base {store6.base} </span> <span> Total {store6.total} </span> </div> ); });
|
MobX + Hooks 写法
为了更好的结合 Hooks 语法,Mobx 在 6.x 中也提供了 2 个新的 API:
useLocalStore
(Hooks 环境下的 observable)
1
| const store = useLocalStore(() => ({ key: "value" }));
|
等价于
1
| const [store] = useState(() => observable({ key: "value" }));
|
为 Hooks 解决了 依赖传递 和 缓存雪崩 的问题。
useObserver
Mobx 使组件响应数据状态的变化主要有以下三种方式:
- @observer
- 给类组件提供 pure component 的能力,将组件的 props 和 state 转换为 observable 态,响应数据变化
- 不推荐在 Hooks 中使用
- observer 方法
- Component:Observer(Mobx 6 中已经基于 useObserver 来实现了)
- Hooks:useObserver
还是以 makeAutoObservable 的 Store 设计为例,这里就不展示了。
我们在函数组件里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| import React from "react"; import { Observer, useLocalObservable, useObserver } from "mobx-react"; import { store6_auto as store6 } from "@/store";
export const MobXHook: React.FC = () => { const store = useLocalObservable(() => store6); return useObserver(() => ( <div> <button onClick={() => { store.setMCount(); }} > count++ </button> <span> Count {store.MCount} </span> <span> Base {store.base} </span> <span> Total {store.total} </span> </div> )); };
export const MobXHook: React.FC = () => { const store = useLocalObservable(() => store6); return ( <Observer> {" "} {() => ( <div> <button onClick={() => { store.setMCount(); }} > count++ </button> <span> Count {store.MCount} </span> <span> Base {store.base} </span> <span> {" "} Total {store.total}{" "} </span> </div> )} </Observer> ); };
|
同时,useLocalObservable 也可以用来创建一个新的 observable,并在组件的整个生命周期内将其保留在组件中(可以理解为组件级别的 observer)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const MouseEventListenerMobx: React.FC = (props) => { const state = useLocalStore( (target) => ({ x: 0, y: 0, handler(e) { const nx = e.xxx; const ny = e.xxx; if ( Math.abs(nx - state.x) >= target.size || Math.abs(ny - state.y) >= target.size ) { state.x = nx; state.y = ny; } }, }), props );
useEffect(() => { document.addEventListener("mousemove", state.handler); return () => document.removeEventListener("mousemove", state.handler); }, []);
return useObserver(() => props.children(state.x, state.y)); };
|
最终推荐方案
综上所述,在结尾给出最终推荐方案。
在 store 设计上,建议采用 makeAutoObservable 的写法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export class Store6_makeAutoObservable { readonly base = 5; MCount = 0;
constructor() { makeAutoObservable(this); }
setMCount() { this.MCount++; }
get total() { return this.MCount * this.base; } }
export default new Store6();
|
在 store 使用上,建议采用 observer 函数包裹组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import React from "react"; import { observer } from "mobx-react"; import { store6_auto as store6 } from "@/store";
const MobXHook: React.FC = () => { const store = store6; return ( <div> <button onClick={() => { store.setMCount(); }} > count++ </button> <span> Count {store.MCount} </span> <span> Base {store.base} </span> <span> Total {store.total} </span> </div> ); };
export default observer(MobXHook);
|