使用 mobx 创建多个商店并将其注入组件的正确方法 - ReactJs

IT技术 reactjs mobx mobx-react state-management
2021-04-12 23:39:51

正如 Mobx文档中所建议的,我以下列方式创建了多个商店:

class bankAccountStore {
  constructor(rootStore){
    this.rootStore = rootStore;
  }
...

class authStore {
  constructor(rootStore){
    this.rootStore = rootStore;
  }
...

最后按以下方式创建根存储此外,我更喜欢在主人的商店构造函数中构造子商店。此外,我发现有时我的子商店必须观察父商店的一些数据,所以我将其传递给子构造函数

class RootStore {
  constructor() {
    this.bankAccountStore = new bankAccountStore(this);
    this.authStore = new authStore(this);
  }
}

下列方式提供给应用程序

<Provider rootStore={new RootStore()}>
  <App />
</Provider>

像这样注入组件

@inject('rootStore') 
@observer
class User extends React.Component{
  constructor(props) {
    super(props);
    //Accessing the individual store with the help of root store
    this.authStore = this.props.rootStore.authStore;
  }
}

即使它需要根存储的一部分,每次将根存储注入到组件中是否是正确有效的方法?如果不是如何将 auth Store 注入用户组件?

编辑:我已经做出了结束 github 讨论的答案。答案中提供的讨论链接

3个回答

这个答案可能是自以为是,但它可能会间接帮助社区。
经过大量研究,我看到了许多人在实践中使用的以下方法。一般方法 有一个根存储,可以作为存储之间的通信通道。

问题一:如何组织stores并注入到组件中?

方法一:

应用程序.js

// Root Store Declaration
class RootStore {
    constructor() {
      this.userStore = new UserStore(this);
      this.authStore = new AuthStore(this);
    }
}    
const rootStore = new RootStore()

// Provide the store to the children
<Provider 
    rootStore={rootStore}
    userStore={rootStore.userStore}
    authStore={rootStore.authStore}
>
  <App />
</Provider>

组件.js

// Injecting into the component and using it as shown below
@inject('authStore', 'userStore')
@observer
class User extends React.Component {
    // only this.props.userStore.userVariable
}

方法二:

应用程序.js

class RootStore {
    constructor() {
      this.userStore = new UserStore(this);
      this.authStore = new AuthStore(this);
    }
} 
const rootStore = new RootStore()

<Provider rootStore={rootStore}>
  <App />
</Provider>

组件.js

// Injecting into the component and using it as shown below
@inject(stores => ({
    userStore: stores.userStore,
    authStore: stores.authStore,
    })
)
@observer
class User extends React.Component {
    // no this.props.rootStore.userStore,userVariable here, 
    // only this.props.userStore.userVariable
}

方法 1 和方法 2 除了语法差异之外没有任何区别。好的!那是注射部分!

问题二:如何进行店间沟通?(尽量避免)

现在我知道一个好的设计可以让商店保持独立,减少耦合。但是以某种方式考虑这样一种情况,UserStore如果更改了某个变量,我希望变量 in发生AuthStore 变化。使用Computed. 这种方法对于上述两种方法都是通用的

授权商店.js

export class AuthStore {    
    constructor(rootStore) {
        this.rootStore = rootStore
        @computed get dependentVariable() {
          return this.rootStore.userStore.changeableUserVariable;                                      
        }
    }
}

我希望这对社区有所帮助。更详细的讨论可以参考我在Github上提出问题

由于这个答案得到了很多赞成,请注意,如果您想在 SSR 项目中使用 mobx,这些方法不适用于序列化程序
2021-06-10 23:39:51

我建议您拥有多个商店,以避免连锁店。正如我们在我们的应用程序中所做的那样:

class RootStore {
    @observable somePropUsedInOtherStores = 'hello';
}

class AuthStore {
    @observeble user = 'Viktor' ; 

    constructor(rootStore) {
        this.rootStore = rootStore;
    }

    // this will reevaluate based on this.rootStore.somePropUsedInOtherStores cahnge
    @computed get greeting() {
        return `${this.rootStore.somePropUsedInOtherStores} ${this.user}`
    }
}

const rootStore = new RootStore();

const stores = {
    rootStore,
    bankAccountStore: new BankAccountStore(rootStore),
    authStore = new AuthStore(rootStore) 
}

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

通过这种方式,您可以准确地访问您需要的商店,因为大多数情况下,一个商店涵盖一个域实例。尽管如此,两个子商店都能够与rootStore. 设置它的属性或调用它的方法。

如果您不需要跨商店通信 - 您可能根本不需要rootStore删除它,不要传递给其他商店。只保留 2 个兄弟商店

回答您关于注入不是整个商店的问题,您可能会从mapperFunction(如mapStateToProps在 redux 中)文档中受益

@inject(stores => ({
        someProp: stores.rootStore.someProp
    })
)
@observer
class User extends React.Component {
// no props.rootStore here, only props.someProp

}
我想指出的主要目标 - 比第三家商店更喜欢跨商店交流,而不是直接交流,因为这将使您能够更好地将商店彼此隔离。我不知道您的应用程序的具体细节,但这是一个开发过程,您会发现某些东西应该以另一种方式工作 - 您的重构。是的,这是开销——但如果你的疮相互依赖——当你不得不重构或移除一个时,预计会有大麻烦。当这对于具有 2 个商店的应用程序可能无效时,但如果您有 20 个 - 这很重要。
2021-06-01 23:39:51
如果没有,您可以rootStore在答案中添加类的声明吗?
2021-06-10 23:39:51
所以这new rootStore()rootStore在问题中声明的同一个的实例正确的?
2021-06-14 23:39:51
除了一件事,我什么都得到了。创建空rootStore然后将它的引用传递给其他商店有什么意义?
2021-06-18 23:39:51
显然rootStore不是空的并且包含一些功能。从关注点分离的最佳做法,这是更好地让bankAccountStore authStore了解rootStore(这将让他们交流),而不是让他们了解彼此。所以rootStore可以起到一个总线的作用,让其他商店进行通信。
2021-06-20 23:39:51

在将 RootStore 传递给 Provider 之前直接在 api.js 文件中初始化它有时不是您想要的。这可以使将 main store 类的实例更难注入其他 js 文件:

示例 1:

app.js - 在将其传递给 Provider 之前创建新实例:

//Root Store Declaration
class RootStore {
    constructor() {
      ...
    }
}    

const rootStore = new RootStore()

// Provide the store to the children
<Provider rootStore={rootStore}>
  <App />
</Provider>

示例 2:

RootStore.js - 直接在 RootStore 类中创建新实例:

// Root Store Declaration
class RootStore {
    constructor() {
      ...
    }
}    

export default new RootStore();

Example 1与 相比Example 2,更难在应用程序的另一部分访问/注入商店,如下Api.js所述。

Api.js 文件代表 axios 包装器(在我的例子中它处理全局加载指示器):

import rootStore from '../stores/RootStore'; //right RootStore is simply imported

const axios = require('axios');
const instance = axios.create({
 ...
});

// Loading indicator
instance.interceptors.request.use(
    (request) => {
        rootStore.loadingRequests++;
        return request;
    },
    (error) => {
        rootStore.loadingRequests--; 
        return Promise.reject(error);
    }
)

使用 React Hooks,你可以通过这种方式注入 store:

import { observer, inject } from "mobx-react";
const YourComponent = ({yourStore}) => {
      return (
          ...
      )
}

export default inject('yourStore')(observer(YourComponent));