Where do model helpers belong in React/Flux?

可紊 提交于 2019-12-24 03:42:28

问题


While attempting to wrap my brain around React and Flux, I am having a hard time deciding where it makes sense to put what I'd call "model helper" methods.

For example, given a situation where there is a "Person" entity contained within a Store, and given that Person has a "first name" and a "last name", where would the most logical place to put a "full name" helper method that simply concatenates the two together? My gut says that it would be best to have "full name" be within the Store, but am not sure. And if so, would it be an Action that updates this value in the store, or should it be computed within the Store itself?

Is there an accepted place to put this kind of functionality?

Thanks!


回答1:


To keep things manageable, especially if you get many stores and a large component tree, try to focus functions of your stores and components:

  1. Stores are for a) storing data (first name, last name, not derived data), and b) for providing components with data (including derived data).
  2. Components are for presenting a) data to the user, and b) anchors for interactions with data.

I would try to avoid manipulating data inside the component tree. And would advise that any data props in any component always originate from a store. They are passed down from higher components, but not manipulated along the way.

If helper functions deal with data only (eg calculating total number of persons in a group), put them in a store. If they deal with presentation logic (e.g. font size of first person on the page should be larger), put them in a separate place. I put them in separate utilities to import. But call these functions only at the lowest possible component.

That way, your code stays much more maintainable.

There is a lot of grey area between data helpers and presentation logic, so your choice in this case is hard to say. But as long as you apply your own logic consistently, your code stays manageable.

That way, when a component gives you issues, it will be easier to trace props to their source, or the function code that was applied to those props in your component.

So maybe a higher order component with a full name function, but I would not have the higher order component create a new prop.




回答2:


So the store holds the application's data and business logic and I see that helper like an action that should take place inside your store. You don't need an action that updates the Full Name, it should be concatenated by the store itself once the first and second name are available.




回答3:


In addition to @Christian's answer (which I agree with) you can use common helpers across Stores by using the object-assign module: https://www.npmjs.com/package/object-assign

This is a partial example of one of my stores with helper methods (e.g. isAuthenticated and getUsername) using object-assign to combine the StatusMixin into every store:

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;
  }
});

and the (full) StatusMixin

'use strict';

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

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

module.exports = StatusMixin;

Now I can can call AuthStore.setStatus(Constants.request.PENDING); (which I do for every Store) without writing the setStatus method on each Store.




回答4:


Generally, "best practice" here is to create a Higher Order Component that provides either the helper function or the concatenated full name as a prop to components that require this modified value.

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)
});

I disagree with @cristian's answer because one of ReactJS's strengths is it's strong separation of concerns and ease of reasoning about application information flow. If we put a helper method in the store, then we don't know when we see full name, if its the full name from the store, or the full name a component created itself by concatenating first name and last name from the same store. However, if don't put this full name function in the store, then we know that any full name comes from a component. Creating a higher order component that can provide this functionality achieves the same DRY principle, while maintaining the ability to clearly reason about where a value/UI element came from.

See https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750 for more info on HoC vs Mixins in React, and why you should probably favor HoCs.



来源:https://stackoverflow.com/questions/32726864/where-do-model-helpers-belong-in-react-flux

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!