Search code examples
javascriptreactjsfluxfluxible

Reactjs, this.context is undfined in constructor method


i m actually trying to develop a simple component corresponding to a list that I fill with one more item when I hit a button.

My problem is that I use ES6, and so I dont use getInitialState, I use the constructor to make the initialization, like explained in the doc.

My problem is that now, this.context is undefined in my constructor, and I cant get my first time array (or the pre-loaded array) directly inside of the consntructor :

import React from 'react';
import ListStore from '../stores/ListStore';

class Client extends React.Component {


  constructor(props){
    super(props);
    this.state = this.getStoreState(); // throw me that in getStoreState, this.context is undefined
  }


  static contextTypes = {
      executeAction: React.PropTypes.func.isRequired,
      getStore: React.PropTypes.func.isRequired
  };

  componentDidMount() {
      this.context.getStore(ListStore).addChangeListener(this._onStoreChange.bind(this));
  }

  componentWillUnmount() {
      this.context.getStore(ListStore).removeChangeListener(this._onStoreChange.bind(this));
  }

  _onStoreChange () {
     this.setState(this.getStoreState());
 }

  getStoreState() {
      return {
          myListView: this.context.getStore(ListStore).getItems() // gives undefined
      }
  }


  add(e){
    this.context.executeAction(function (actionContext, payload, done) {
        actionContext.dispatch('ADD_ITEM', {name:'toto', time:new Date().getTime()});
    });
  }

  render() {
      return (
          <div>
              <h2>Client</h2>
              <p>List of all the clients</p>
              <button onClick={this.add.bind(this)}>Click Me</button>
              <ul>
                  {this.state.myListView.map(function(test) {
                    return <li key={test.time}>{test.name}</li>;
                  })}
              </ul>
          </div>
      );
  }
}


export default Client;

I just want to pre-load the array in the constructor even if its empty or not, and that's exactly what my store returns :

import BaseStore from 'fluxible/addons/BaseStore';

class ListStore extends BaseStore {

  constructor(dispatcher) {
      super(dispatcher);
      this.listOfClient = [];
    }

  dehydrate() {
      return {
          listOfClient: this.listOfClient
      };
  }

  rehydrate(state) {
      this.listOfClient = state.listOfClient;
  }


  addItem(item){
    this.listOfClient.push(item);
    this.emitChange();
  }

  getItems(){
    return this.listOfClient;
  }

}

ListStore.storeName = 'ListStore';
ListStore.handlers = {
    'ADD_ITEM': 'addItem'
};

export default ListStore;

Thanks for your help


Solution

  • The problem you are facing is because your component should be stateless, I can't say this enough, state should live in your stores, (UI-state, "could" live in your components but thats debatable)

    What you should do is use a higher level component, wrap your react component within a higher level component, let that component fetch the state from the store, and pass it as props to your component.

    This way you don't need initial state, you can just set your defaultProps, and propTypes.

    This way your component is stateless, and you could make complete use of the react lifecycle, it also become reusable, because you don't have the logic of fetching the actual data within your component.

    Good reads

    Hope this helps :)