为什么我需要在MobX中使用上下文或提供程序?

时间:2019-08-21 10:43:03

标签: reactjs mobx mobx-react

我正在尝试学习如何将MobX与React一起使用,并且我不明白为什么如果需要保留状态的对象永远不变,则仅使用其内容就必须使用Provider或Context。

例如我在store.js中有一个商店(一个随时间变化的简单计时器):

import { decorate, observable, action } from 'mobx';
import React from 'react';

class TheTimer {
    currentTick = 0; // In seconds since start

    tick = () => {
        this.currentTick = Math.floor(performance.now() / 1000);
    }
}

decorate(TheTimer, {
    currentTick: observable,
    tick: action
});

// Bare store
export const timerStore = new TheTimer();
// Store as React context
export const TimerStoreContext = React.createContext(timerStore);

// This makes the timer go
setInterval(timerStore.tick, 100);

现在在组件中使用裸存储没有问题:

import React from 'react';
import { configure } from 'mobx';
import { observer } from 'mobx-react';

import { timerStore } from './store';

configure({ enforceActions: 'observed' });

const App = observer(() => {
    return (
        <p>{timerStore.currentTick}</p>
    );
});

export default App;

使用上下文也可以:

import React from 'react';
import { configure } from 'mobx';
import { observer } from 'mobx-react';

import { TimerStoreContext } from './store';

configure({ enforceActions: 'observed' });

const App = observer(() => {
    const timerStore = React.useContext(TimerStoreContext);

    return (
        <p>{timerStore.currentTick}</p>
    );
});

export default App;

(我正在使用create-react-app加上mobx,mobx-react,即带有MobX 5.13.0和mobx-react 6.1.3的React 16.9.0)

请注意,商店是一次创建的,从那以后它始终是同一对象。

当直接将商店用作全局变量时,为什么每个人(或较早的基于mobx-react Provider的解决方案)都使用Context?

仅仅是可测试性吗?

请注意,我还有非React的JS代码,应用程序通过Websocket与服务器通信,服务器的更新也将导致调用操作;我计划为此使用裸存储,因为该代码位于React组件之外。

2 个答案:

答案 0 :(得分:1)

上下文主要用于需要不同嵌套级别的许多组件访问某些数据的情况。谨慎地应用它,因为它会使组件的重用变得更加困难。

Provider允许使用组件订阅上下文更改。

在Mobx中,我们在顶层使用提供程序将所有商店实例传递给使用提供程序包装的所有子组件,例如

import { Provider } from "mobx-react";


<Provider {...Stores}>
   <App/>
 </Provider>

现在,我们可以使用注入Hoc例如

将子组件内部的所有商店属性作为道具访问。
class App extends Component {
  render() {
    return <div>app</div>;
  }
}

export default inject("app")(App);

您还可以使用Inject Hoc来访问多个商店中的属性,从而注入多个商店

inject(stores => ({
 abc: stores.abc,
 bca: stores.bca
}))

因此它将解决直接在组件内部导入商店的问题,mobx也不建议

答案 1 :(得分:1)

前一段时间,我对此问题感兴趣。我从维护者那里得到的答案是由于代码的可测试性

使用示例中的代码,您基本上将TheTimer类视为单例。

如果您真的不喜欢使用React.context,也可以改用service locator pattern(但基本上是同一回事)

export const ListHolder = observer(function(props) {
  const listStore = serviceLocator.get(STORES.LIST_STORE)
  return (
    <div>
      <div className={styles.wrap}>
        {listStore.lists.map(list => {
          return <List key={list.id} list={list} />
        })}
      </div>
    </div>
  )
})

也请看一下MobX存储库上有关这个问题的漫长而有趣的讨论。

suggested best practice for holding onto a store constructed with props