[React] Mobx 入门

2,281 阅读3分钟

MobX 入门

参考文档

官方文档:mobx.js.org/getting-sta…

中文翻译:juejin.cn/post/684490…

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;