与 React 集成
MobX 与 React 继承的时候可以选择使用 mobx-react-lite
或者使用 mobx-react
库。现在一般推荐采用 mobx-react-lite
库,因为 mobx-react
库主要适用于旧版本的 MobX 和 React,而 mobx-react-lite
更加轻量一些。
与 React 集成的时候,主要是利用 observer()
函数生成 React 高阶组件。observer()
创建的高阶组件将自动订阅 React 组件在任何渲染期间使用的 Observable 对象。observer()
会确保当 Observable 发生变化的时候组件才会自动进行重新渲染,并且在更新 Observable 对象中的不可见对象的时候,重新渲染的操作也不会进行。这样一来,使用 MobX 的应用性能就会得到提升。
observer()
并不关心 Observable 对象是如何传递到组件的,这些对象可以通过任意渠道传递给对象,例如 props
、Context
等,即便是在 State 中深层嵌套存放也不影响其正常使用。
以下是一个简单的与 React 集成的示例。
import React from 'react';
import ReactDom from 'react-dom';
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';
class Timer {
seconds = 0;
constructor() {
makeAutoObservable(this);
}
increase() {
this.seconds++;
}
}
const timer = new Timer();
const TimerView = observer(({ timer }) => <div>Time passed: {timer.seconds}</div>);
ReactDom.render(<TimerView timer={timer} />, document.body);
setInterval(() => timer.increase(), 1000);
或者还可以使用 Context 来实现 Observable 的传递。
import { createContext, useContext } from 'react';
const TimerContext = createContext<Timer>();
const TimerView = observer(() => {
const timer = useContext<Timer>(TimerContext);
return <div>Time passed: {timer.seconds}</div>;
});
ReactDom.render(
<TimerContext.Provider value={new Timer()}>
<TimerView />
</TimerContext.Provider>
);
这里并不推荐把每一个 Observable 都使用一个 Context Provider 来提供,可以把该级别所需要的 Observable 集合起来一并在 Context 中提供,具体示例可见后面“定义数据存储”一节。
observable()
可以监控的 Observable 可以来自任何地方,甚至是组件的本地 State,在这种用法下,useState()
函数的返回值就可以少用一个了。
const LocalTimerView = observer(() => {
const [timer] = useState(() => new Timer());
useEffect(() => {
const handle = setInterval(() => {
timer.increase();
}, 1000);
return () => {
clearInterval(handle);
};
}, [timer]);
return <div>Time passed: {timer.seconds}</div>;
});
示例中的这种 useState()
的使用形式似乎不是太 React,所以 MobX 提供了一个专用的自定义 Hook。
import { observer, useLocalObservable } from 'mobx-react-lite';
const LocalTimerView = observer(() => {
const timer = useLocalObservable(() => ({
seconds: 0,
increase() {
this.seconds++;
}
}));
// 执行其他副作用函数或者渲染过程
});
这里有一点需要注意一下,我们在构建列表时常常用到的回调组件通常也是组件渲染周期的一部分,所以通常这些回调组件也必须是一个 observer
组件。这个时候就需要使用 <Observer />
创建一个匿名观察者。
const DataView = observer(({ item }: { item: data }) => {
return <DateRow onRender={() => <Observer>{() => <td>{data.content}</td>}</Observer>} />;
});
另外,有些时候使用 props
接受到的参数并不是 Observable,这就需要在使用的时候用 useLocalObservable
包装一下,但是对于这样传递过来的 props
依旧不会在其变化时得到响应。在这种情况下,就需要定义一个 useEffect
手动更新一下。