Search code examples
reactjsmixinsecmascript-5higher-order-components

React: Higher Order Components: Can you can childs state


I'm trying to refactor from mixins to HOCs so I can upgrade to the newest React (I'm currently using React 13/ES5). When my component loads, the mixin is able to access a data object, this.data, that has a query property with a route.

React.createClass({
  mixins: [myMixin],
  data: {
    query: "/api/foo"
  },
  componentDidUpdate(): function(){
  }
  ...
}

On componentDidMount inside the mixin, it would query that route, get back the data and put it on state.

var myMixin = React.createClass({
  componentDidMount(): function(){
    this.fetchData(this.data.query)
  }
  fetchData(query) {
    // fetch the data
  }
  ...
}

This extracts a lot of boilerplate from my components. I don't have to run a componentDidMount on every component, just have the data object.

I have this mixin scattered all over the project. It actually does a lot more than this but I'm just trying to dumb it down for example sake.

In comes HOCs. I took the all the stuff from data and put in into getInitialState. Now that I have data on the now wrapped component's state, how do I access it? Like my mixin, I want my HOC to handle my componentDidMount, get access to data.query and fetch the data. I don't want to have to rewrite every component to have a componentDidMount.

PS- I normally use the newest version of React and ES6, if anyone can tell me why the mixin could do this.data and my component couldn't that would be very helpful. In the newest version of React you can't define an object outside of render() like that.


Solution

  • React doesn't encourage to change the state from outside of the component. The state is owned by component and so only the component has control over it.

    From React documentation:

    State is similar to props, but it is private and fully controlled by the component.

    So what you can do is use props instead of state. For the given example you can write Higher Order Component as follows. But your Child component should be altered to render data from props instead of state.

    function HOC(WrappedComponent){
    
      return React.createClass({
    
        getInitialState: function() {
          return { data: null };
        },
    
        componentDidMount: function(){
          this.fetchData(WrappedComponent.prototype.data.query)
        },
    
        fetchData: function(query){
         // Fetching data first
         // Then set the state with data
         this.setState({data:data});
        },
    
        render: function() {
          var props = Object.assign({}, this.props, this.state);
          return React.createElement(WrappedComponent, props);
        }
    
      });
    
    }
    
    var HOCChild = HOC(Child);
    

    PS- I normally use the newest version of React and ES6, if anyone can tell me why the mixin could do this.data and my component couldn't that would be very helpful. In the newest version of React you can't define an object outside of render() like that.

    Usually, React Component define as an ES6 class. ES6 classes don't support static or instance properties. But you can define instance properties inside the contractor like this.

    class A{
      constructor(){
        this.foo = "foo"
      }
    }