Search code examples
reactjsreactjs-flux

react component state not updating (FLUX App)


I'm trying to hand build a react/flux app, roughly following: https://scotch.io/tutorials/getting-to-know-flux-the-react-js-architecture

I've got most of it working but when I update the state the component doesn't re-render. Can anyone help?

Thanks. :)


Repo is https://github.com/jmsherry/flux_hand

CounterStore.js

import Dispatcher from 'Flux';
import AppDispatcher from './../dispatcher/AppDispatcher';
import CounterConstants from '../constants/constants';
import { EventEmitter } from 'events';
import assign from 'object-assign';


let _count = 5;

function increment() {
  _count +=1;
}

function decrement() {
  _count -=1;
}

let CounterStore = assign({}, EventEmitter.prototype, {

  getCount() {
    return _count;
  },

  emitChange() {
    this.emit(CounterConstants.CHANGE_EVENT);
  },

  /**
   * @param {function} callback
   */
  addChangeListener(callback) {
    this.on(CounterConstants.CHANGE_EVENT, callback);
  },

  /**
   * @param {function} callback
   */
  removeChangeListener(callback) {
    this.removeListener(CounterConstants.CHANGE_EVENT, callback);
  },

  dispatcherIndex: AppDispatcher.register(function(payload) {
    console.log('blah', arguments);
    var action = payload.action;
    var text;

    switch(action.actionType) {
      case CounterConstants.INCREMENT:
          console.log('inc', _count);
          increment();
          console.log(_count);
          CounterStore.emitChange();
        break;

      case CounterConstants.DECREMENT:
      console.log('dec', _count);
        decrement();
        console.log(_count);
        CounterStore.emitChange();
        break;
    }

    return true; // No errors. Needed by promise in Dispatcher.
  })

});

export default CounterStore;

AppDispatcher.js

import { Dispatcher } from 'Flux';

const AppDispatcher = new Dispatcher();

AppDispatcher.handleViewAction = function(action) {
  console.log('in', arguments);
  this.dispatch({
    source: 'VIEW_ACTION',
    action
  });
}

export default AppDispatcher;

constants.js

import keyMirror from 'keymirror';

const CounterConstants = keyMirror({
  INCREMENT: null,
  DECREMENT: null
});

CounterConstants.CHANGE_EVENT = 'change';

export default CounterConstants;

actions.js

import AppDispatcher from '../dispatcher/AppDispatcher';
var CounterConstants = require('../constants/constants');

const CounterActions = {

  /**
   * @param  {string} text
   */
  increment() {
    AppDispatcher.handleViewAction({
      actionType: CounterConstants.INCREMENT
    });
  },

  decrement() {
    AppDispatcher.handleViewAction({
      actionType: TodoConstants.DECREMENT
    });
  }

};

export default CounterActions;

counter.js <-- parent view

import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import CounterConstants from './../constants/constants';
import AppDispatcher from './../Dispatcher/AppDispatcher.js';
import Controls from './Controls';
import Display from './Display';

class Counter extends Component {
  render(){
    return (
      <div className="counter">
        <h1>My counter</h1>
        <Display />
        <Controls />
      </div>
    )
  }
}

export default Counter;

controls.js

import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import CounterConstants from './../constants/constants';
import AppDispatcher from './../dispatcher/AppDispatcher';
import CounterActions from './../actions/actions';

class Controls extends Component {
  render(){
    console.log('here', AppDispatcher);
    return (
      <div className="controls">
        <button onClick={CounterActions.increment}>+</button>
        <button onClick={CounterActions.decrement}>-</button>
      </div>
    )
  }
}

export default Controls;

display.js

import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import CounterConstants from './../constants/constants';
import CounterStore from './../stores/CounterStore';

// Method to retrieve application state from store
function getAppState() {
  console.log('getting app state...');
  return {
    count: CounterStore.getCount()
  };
}

class Display extends Component {
  constructor(props) {
    super(props);
    this.state = getAppState();
  }

  // Update view state when change event is received
  _onChange() {
    console.log('prechange', this.state);
    const newState = getAppState();
    console.log('newState', newState);
    (newState) => this.setState;
  }

  // Listen for changes
  componentDidMount() {
    CounterStore.addChangeListener(this._onChange.bind(this));
  }

  // Unbind change listener
  componentWillUnmount() {
    CounterStore.removeChangeListener(this._onChange.bind(this));
  }

  shouldComponentUpdate( newProps, newState ) {
    console.log('shouldComponentUpdate', arguments);
  }

  render() {
    let count = getAppState().count;
    console.log('rendering', count, this.state);
    return (
      <div className = "display" >
        <p>State: { this.state.count }</p>
        <p>count: { count }</p>
      </div>
    )
  }
}

export default Display;

Solution

  • OK! So, I was having issues with this binding, so I used an es6 arrow function. I'm new to some bits of es6 and what I've got there won't work. That's not how you execute arrow functions, that's why it's not working...