MobX 入门
参考文档
mobx-react 5.x 迁移至 mobx-react 6.x:mobx-react.js.org/recipes-mig…
关键术语翻译:
| 英语 | 中文 |
|---|---|
| observable | 可观察的 |
| observer | 观察者 |
学习 observable 和 observer
1. 抛开mobx,先看个例子
const person = {
name: 'John',
// getter 方法,读取 welcomeTip 属性时执行
get welcomeTip() {
console.log('run get welcomeTip');
return 'Welcome, ' + this.name
},
setName(name) {
this.name = name
}
};
const MobXExam = () => {
return (
<div>
<p>{person.welcomeTip}</p>
<input onBlur={e => e.target.value && person.setName(e.target.value)} placeholder="change name"/>
<button type="button" onClick={() => console.log(person.name)}>log person.name</button>
<button type="button" onClick={() => console.log(person.welcomeTip)}>log person.welcomeTip</button>
</div>
)
};
export default MobXExam;
界面与操作如下:
结论:
MobXExam 组件可以读取和改变 person 对象;
person对象属性改变后,组件中的dom没有更新;
2. 使用 observable 和 observer 改造例子
import { observable } from 'mobx';
import { observer } from 'mobx-react-lite';
const person = observable({
// name...
});
const MobXExam = observer(() => {
// return ...
});
export default MobXExam;
用法很简单,使用 observable 包裹原对象,使用 observer 包裹原组件,刷新页面后仿照改造前操作后发现,person 对象改变后,网页中的 “Welcome, John” 自动更新成了 ”Welcome, Tom”。
observable 和 observer 只能结合使用,缺少任何一个都会失去响应性。
3. 增加一个 observable 对象
const person2 = observable({
name: 'John'
});
const MobXExam = observer(() => {
return (
<div>
{/*other code*/}
<p>{person2.name}</p>
<input onBlur={e => e.target.value && (person2.name = e.target.value)} placeholder="change name"/>
</div>
)
});
结论:一个 observer 可以观察多个 observable 对象。
4. 增加一个 observer 组件
const person = observable({ name: 'John' });
const PersonName = observer(() => <p>{person.name}</p>);
const MobXExam = observer(() => {
return (
<div>
<PersonName/>
<p>{person.name}</p>
<input onBlur={e => e.target.value && (person.name = e.target.value)} placeholder="change name"/>
</div>
)
});
结论:多个 observer 可以观察多个 observable 对象。
以上,observable 对象均为全局对象,所有组件都可以读到,下面开始尝试在组件内部创建局部可观察对象。
React Hook 组件中如何使用MobX
创建局部可观察对象 - useLocalStore
useLocalStore hook创建一个局部可观察store,该store将在组件整个生命周期内保持可观察状态。
useLocalStore 是基于observable的封装,下面两种写法作用是一样的
const [person] = useState(() => observable({ name: 'John' }));
const person = useLocalStore(() => ({ name: 'John' }));
三种 observe 方式
1. observer HOC
import { useLocalStore, observer } from 'mobx-react-lite';
const MobXExam = observer(() => {
const person = useLocalStore(() => ({
name: 'John',
get welcomeTip() {
console.log('run get welcomeTip');
return 'Welcome, ' + this.name
},
}));
return (
<div>
<p>{person.name}</p>
<p>{person.welcomeTip}</p>
<input onBlur={e => e.target.value && (person.name = e.target.value)} placeholder="change name"/>
</div>
)
});
export default MobXExam;
2. Observer component
import { useLocalStore, Observer } from 'mobx-react-lite';
const MobXExam = () => {
// const person = ...
return (
<div>
<Observer>{() => <p>{person.name}</p>}</Observer>
<p>{person.welcomeTip}</p>
{/*<input ...*/}
</div>
)
};
3. useObserver hook
import { useLocalStore, useObserver } from 'mobx-react-lite';
export const MobXExam = () => {
// const person = ...
return useObserver(() => (
{/*<div ...*/}
))
};
三种 observe 方式对比:
observer HOC 和 useObserver hook 会在数据发生变化时重新渲染整个组件;
Observer component 可以做到局部渲染。
mobx 6.x 以前的用法Provider and inject(了解即可)
import { Provider, inject, observer } from 'mobx-react';
import { observable } from "mobx";
const PersonName = inject('user')(
observer(({ user }) => {
return (<p>{user.name}</p>);
})
);
function Person() {
return <PersonName/>;
}
function MobXExam() {
const person = observable({ name: 'John' });
const toggleName = () => {
person.name = person.name === 'John' ? 'Tom' : 'John';
};
return (
<div>
<Provider user={person}>
<Person/>
</Provider>
<button type="button" onClick={() => toggleName()}>Change Name</button>
</div>
)
}
export default MobXExam;
MobXProviderContext (了解即可)
mobx-react@6 新增 Context对象,下面示例为读取common模块中的lang属性
import { observer, MobXProviderContext } from 'mobx-react';
import { useContext } from 'react';
function useStores() {
return useContext(MobXProviderContext);
}
const MobXExam = observer(() => {
const { common } = useStores();
return (
<div>
common.lang: {common.lang}
</div>
)
});
export default MobXExam;