模型助手在 React/Flux 中属于什么位置?

IT技术 javascript reactjs reactjs-flux flux
2021-05-26 00:49:57

在尝试将我的大脑围绕在 React 和 Flux 上时,我很难决定将我所谓的“模型助手”方法放在哪里更有意义。

例如,假设 Store 中包含一个“Person”实体,并且该 Person 具有“名字”和“姓氏”,那么放置“全名”助手最合乎逻辑的位置简单地将两者连接在一起的方法?我的直觉说最好在 Store 中有“全名”,但我不确定。如果是这样,它会是一个更新 store 中这个值的 Action,还是应该在 Store 本身内计算它?

有没有可以接受的地方放置这种功能?

谢谢!

4个回答

为了使事情易于管理,特别是如果您有许多商店和大型组件树,请尝试关注商店和组件的功能:

  1. 存储用于 a) 存储数据(名字、姓氏、非派生数据),以及 b)为组件提供数据(包括派生数据)。
  2. 组件用于向用户呈现 a) 数据,以及 b) 用于与数据交互的锚点。

我会尽量避免在组件树中操作数据。并建议任何组件中的任何数据props始终来自商店。它们从更高的组件传递下来,但不会在此过程中被操纵。

如果辅助函数只处理数据(例如计算组中的总人数),则将它们放在存储中。如果他们处理呈现逻辑(例如页面上第一人称的字体大小应该更大),请将它们放在一个单独的地方。我将它们放在单独的实用程序中进行导入。但仅在尽可能低的组件上调用这些函数。

这样,您的代码就更易于维护了。

数据助手和表示逻辑之间有很多灰色地带,所以你在这种情况下的选择很难说。但是只要您始终如一地应用自己的逻辑,您的代码就可以保持可管理性。

这样,当组件给您带来问题时,将更容易跟踪 props 到它们的来源,或者应用于组件中这些 props 的函数代码。

所以也许是一个具有全名函数的高阶组件,但我不会让高阶组件创建一个新的props。

所以 store 保存了应用程序的数据和业务逻辑,我看到这个 helper 就像一个应该在你的 store 内发生的动作。您不需要更新全名的操作,一旦第一个和第二个名称可用,它应该由商店本身连接。

除了@Christian 的回答(我同意)之外,您还可以使用以下object-assignmodule在商店中使用通用助手https : //www.npmjs.com/package/object-assign

这是我的一个商店的部分示例,其中包含帮助方法(例如isAuthenticatedgetUsernameobject-assign用于将 组合StatusMixin到每个商店中:

var AuthStore = assign({}, StatusMixin, EventEmitter.prototype, {
  isAuthenticated: function () {
    return _data.get(TOKEN_KEY) ? true : false;
  },

  getUsername() {
    return _data.get(USERNAME_KEY);
  },

  getToken() {
    return _data.get(TOKEN_KEY);
  },

  invalidate() {
    _data = _data.clear(); 
    this.setStatus(''); //this method is from the StatusMixin!
    this.emitChange(Constants.CHANGED);
  },

  emitChange: function() {
    LocalStorage.set(Constants.ls.AUTH_STORE, {
      auth_token: _data.get(TOKEN_KEY),
      username: _data.get(USERNAME_KEY)
    });
    this.emit(Constants.CHANGED);
  },

  addChangeListener: function(callback) {
    this.on(Constants.CHANGED, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(Constants.CHANGED, callback);
  },

  getState: function()  {
    return _data;
  }
});

和(完整的) StatusMixin

'use strict';

var logger = require('../../util/Logger');

var StatusMixin = {
  _status: '',
  getStatus: function() {
    return this._status;
  },
  setStatus(status) {
    this._status = status;
  }
};

module.exports = StatusMixin;

现在我可以调用AuthStore.setStatus(Constants.request.PENDING);(我对每个 Store 都这样做)而无需setStatus在每个 Store 上编写方法。

通常,此处的“最佳实践”是创建一个高阶组件,该组件提供辅助函数或连接的全名作为需要此修改值的组件的props。

function giveFullName(Component) {
  const ComponentWithFullName = React.createClass({
    render() {
      return <Component {...this.props} fullName={this.props.firstName+" "+this.props.lastName} />;
    }
  });
  return ComponentWithFullName;
};

var PersonPage = React.createClass({

  render() {
    var { name } = this.props.fullName; // get fullName from props
    return <div>{'Hello '+(name ? name : 'Mystery Stranger')}</div>;
  }
});
PersonPage = ComponentWithFullName(PersonPage)
});

我不同意@cristian 的回答,因为 ReactJS 的优势之一是它对应用程序信息流的关注点分离和推理的简易性。如果我们在 store 中放置一个 helper 方法,那么我们不知道什么时候会看到全名,它是来自 store 的全名,还是一个组件通过连接来自同一个 store 的名字和姓氏而创建的全名. 但是,如果不把这个全名函数放在 store 中,那么我们就知道任何全名都来自一个组件。创建可以提供此功能的高阶组件实现了相同的 DRY 原则,同时保持了清晰推理值/UI 元素来自何处的能力。

请参阅https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750了解有关 React 中的 HoC 与 Mixins 的更多信息,以及为什么您应该偏爱 HoC。