模型助手属于 React/Flux 的什么地方?

Where do model helpers belong in React/Flux?

在尝试全神贯注于 React 和 Flux 时,我很难决定将我称之为 "model helper" 的方法放在哪里才有意义。

例如,假设 Store 中包含一个 "Person" 实体,并且假设 Person 有一个 "first name" 和一个 "last name",最合乎逻辑的是放一个 "full name" 简单地将两者连接在一起的辅助方法的地方?我的直觉告诉我最好让 "full name" 在商店中,但我不确定。如果是这样,它是在商店中更新此值的操作,还是应该在商店本身内计算?

是否有可接受的地方放置这种功能?

谢谢!

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

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

这是我的一个商店的部分示例,它使用辅助方法(例如 isAuthenticatedgetUsername)使用 object-assignStatusMixin 组合到每个商店中:

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 都这样做)而无需在每个 Store 上编写 setStatus 方法。

一般来说,"best practice"这里是创建一个高阶组件,它提供辅助函数或连接的全名作为需要此修改值的组件的道具。

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中,那么我们就知道任何全名都来自于一个组件。创建可以提供此功能的高阶组件实现相同的 DRY 原则,同时保持清楚地推断 value/UI 元素来自何处的能力。

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

为了使事情易于管理,尤其是当您拥有许多商店和大型组件树时,请尝试关注您的商店和组件的功能:

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

我会尽量避免在组件树中操作数据。并且会建议任何组件中的任何数据道具始终来自商店。它们是从更高的组件传下来的,但并没有被操纵。

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

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

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

这样,当一个组件给您带来问题时,可以更容易地追踪 props 的来源,或者在您的组件中应用到这些 props 的函数代码。

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