Skip to content Skip to sidebar Skip to footer

Where Do Model Helpers Belong In React/flux?

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 si

Solution 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.

Solution 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.

Solution 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:

varAuthStore = 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');

varStatusMixin = {
  _status: '',
  getStatus: function() {
    returnthis._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.

Solution 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 propsreturn <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.

Post a Comment for "Where Do Model Helpers Belong In React/flux?"