I have a Flux problem that's been killing me. I'm calling an action on page load, but for some reason it doesn't update the state in the component. In this example, I have this.props.count
set to 5 (the default in TestStore). I then call an action to increase it in componentDidmount
to 6, but it doesn't update the component's state. It stays at 5. Then if I click the link to manually update it, it goes from 5 to 7.
I think it has something to do with the Flux changeListener being added to the top-level component after the action is dispatched?
If I put the changeListener in componentWillMount
instead of componentDidMount
in the top-level component, then everything works. But that doesn't seem like the proper way? I feel like I'm missing something.
Here's a console.log and the components...
< Tester />
import React from 'react';
import TestActions from '../actions/TestActions';
export default class Tester extends React.Component {
componentDidMount() {
// this.props.count defaults to 5
// This brings it to 6
TestActions.increaseCount();
}
render() {
return (
<div>
// Count should display 6, but shows 5
Count: {this.props.count}
<br />
<a href="#" onClick={this._handleClick}>Increase</a>
</div>
);
}
_handleClick(e) {
e.preventDefault();
TestActions.increaseCount();
}
}
< Application />
import React from 'react';
import {RouteHandler} from 'react-router';
import TestStore from '../stores/TestStore';
export default class Application extends React.Component {
constructor() {
super();
this._onChange = this._onChange.bind(this);
this.state = this.getStateFromStores();
}
getStateFromStores() {
return {
count: TestStore.getCount()
};
}
componentDidMount() {
TestStore.addChangeListener(this._onChange);
}
_onChange() {
this.setState(this.getStateFromStores());
}
componentWillUnmount() {
TestStore.removeChangeListener(this._onChange);
}
render() {
return (
<RouteHandler {...this.state} {...this.props}/>
);
}
}
TestStore
var AppDispatcher = require('../dispatchers/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var TestConstants = require('../constants/TestConstants');
var assign = require('object-assign');
var CHANGE_EVENT = 'change';
var _count = 5;
function increaseCount() {
_count = _count + 1;
}
var TestStore = assign({}, EventEmitter.prototype, {
getCount: function() {
return _count;
},
emitChange: function() {
console.log('TestStore.emitChange');
this.emit(CHANGE_EVENT);
},
addChangeListener: function(callback) {
console.log('TestStore.addChangeListener');
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
});
AppDispatcher.register(function(action) {
var text;
switch(action.actionType) {
case TestConstants.INCREASE_COUNT:
increaseCount();
TestStore.emitChange();
break;
default:
// no op
}
});
module.exports = TestStore;
As you said, the issue is in
<Application />
: You start listening to the store incomponentDidMount
, whereas you should do that incomponentWillMount
, otherwise you start listening to changes after all the components are mounted, therefore you lose the initial increment.Anyway, I would suggest to perform the action in the top component:
In
<Application />
In
<Tester/>