I am pretty new to FLUX, so I started with the TODO example on their website:
https://facebook.github.io/flux/docs/todo-list.html
In the Listening to Changes with a Controller-View part:
There is on event binding in TodoApp:
componentDidMount: function() {
TodoStore.addChangeListener(this._onChange);
}
and this._onChange is like:
_onChange: function() {
this.setState(getTodoState());
}
The addChangeListener from TodoStore is like:
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
}
What confused me here is:
It seems that the store just simply register that _onChange(), but how does the TodoStore know whose _onChange() need to be called if there are multiple TodoApps rendered on the page.
TodoStore simply emits the event and every component that is listening to that store will update. So if you have two TodoApps on page, both will update.
Related
I would like to have a component, which get the property from parent component and make an AJAX request, based on this propery. The parent component can change this property and my child component must get another one AJAX request.
Here is my code, but I am not sure it is optimal and even correct:
<News source={this.state.currentSource} />
Component:
var News = React.createClass({
propTypes: {
source: React.PropTypes.string
},
getInitialState: function() {
return {
entities: []
};
},
componentWillReceiveProps(nextProps) {
var url = 'http://localhost:3000/api/sources/' + nextProps.source + '/news';
this.serverRequest = $.get(url, function(result) {
this.setState({
entities: result
});
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
// ...
}});
module.exports = News;
Does componentWillReceiveProps is a good place for this code?
componentWillReceiveProps will work just fine, one thing I would do differently is if <News/> is getting its props from a parent component then a good pattern to follow is to have the parent component actually handle the api calls and pass down the results as props. This way your News component really only has to deal with rendering content based on its props vs rendering and managing state.
I can only see limited portion of your App so that might not fit your use case but here is a great article on doing that type of differentiation between smart and dumb components.
http://jaketrent.com/post/smart-dumb-components-react/
Consider a component in React which has its own state and also uses some shared state (shared states are stored within a Store).
Here's a sample component for better understanding the question:
var ControllerView = React.createClass({
getInitialState: function() {
return {privateState: 1, sharedState: -1};
},
componentDidMount: function() {
Store.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
Store.removeChangeListener(this._onChange);
},
_onChange: function() {
this.setState({
privateState: this.state.privateState,
sharedState: Store.getSharedState()
});
}
stepForward: function() {
this.setState({
privateState: this.state.privateState + 1,
sharedState: this.state.sharedState
});
Action.decrease();
},
render: function() {
return (
<div>
<button onClick={this.stepForward}>Go forth with private and shared state</button>
<div>Private State: {this.state.privateState}</div>
<div>Shared State: {this.state.sharedState}</div>
</div>
);
}
});
As you can see in the given code, there's a button which pressing it will result in changing both private state and shared state. A private state can simply be updated by calling the setState method. But following the Flux architecture, updating a store should go through actions. That's why there's a Action.decrease();.
Just a recap of what will happen when an action is called;
The action method called will come up with the new data (either through calculation or calling a service, in my question it doesn't matter how you come up with the new data).
Once the action has got the new data, it will dispatch it so any store interested in it and they will pick it up and store it.
After any store saves the data, it will emit a change notifying any component registered within that store to pick up the new data. This is when the component's _onChange is called.
So when the button is pressed, the component's state will update twice, once to update the private state and the second time when _onChange is called to update the shared state. These two setState calls happen one after the other in one thread.
What I experienced is that in such cases only the last setState will apply and the previous ones are discarded. My question is how to overcome this problem?
When calling setState, you should only include the properties you are actually updating - other properties in the state will be left alone. You are seeing the fact that, when setState is called, this.state is not immediately updated, but only once the actual react render starts (immediately after componentWillUpdate() and before render() I think). So when you call your second setState, you are replacing the new value for privateState with the old one (which is still held in this.state until the render happens). So, your code should look like:
_onChange: function() {
this.setState({
sharedState: Store.getSharedState()
});
}
stepForward: function() {
this.setState({
privateState: this.state.privateState + 1,
});
Action.decrease();
}
I've been playing a bit with ReactJS and am really enjoying the framework.
I'm also trying to follow the rule of creating components that are stateless where possible.
I have a Settings component that includes a child SettingsForm and a SettingsWidget.
Settings holds all the states, and only pass it as props to the form and widget.
This works (and scales) well because when the state in Settings is updated, it propagates to all child components.
var Settings = React.createClass({
getInitialState: function() {
settings: {}
}
})
What I am not 100% sure on is the best practice when accessing input values on SettingsForm to pass it on to the parent component.
I know I can use refs and also two-way binding to accomplish this, but neither feel very "ReactJS-like".
Is there a better of way accomplishing this that I am unaware of? For the sake of completeness, I've included the relevant code in my SettingsForm component below
var SettingsForm = React.createClass({
getInitialState: function() {
return {
changed: false
}
},
handleChange: function(event) {
this.setState({changed: true})
this.props.handleChange(
this.refs.emailInputFieldRef.getDOMNode().value,
this.refs.firstNameInputFieldRef.getDOMNode().value
)
},
handleSubmit: function(event) {
event.preventDefault();
// Access and pass on input values to parent callback so state is updated
this.props.handleUpdate(
this.refs.emailInputFieldRef.getDOMNode().value,
this.refs.firstNameInputFieldRef.getDOMNode().value
)
this.setState(this.getInitialState());
},
...
}
For now there is a Mixin you can use to link the input values to the state, called LinkedStateMixin that is exactly what you are looking for...
var WithLink = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
},
render: function() {
return <input type="text" valueLink={this.linkState('message')} />;
}
});
Then all you have to do is modify your handler functions on the parent component to take your inputs as variables, and pass that function down to the child component as a prop. When you want to handle the form, call that function in the props and send the state (bound with from the Mixin) as the variables.
React Docs - React Link
I have a store (MovieStore) which I would like to use as a central store to movies in my React App. I have a search page, that lists the movies based on a search string (and uses of course the MovieStore).
In addition I have a typeahead component, that makes suggestions based on the string the user typed into the search box. I want to use the same MovieStore for the typeahead too.
The problem is, that in this case the MovieStore should represent two states at the same time (the result list from the previous search, and the typeahead suggestions based on the current search string)
I ended up emitting two different kinds of events from the store, the typeahead listening to one event, the result list listening to the other, but it feels awkward. (because at the end of the day, one of the two components get out of synch, it just does not listen to a specific kind of event deliberately).
What better solution would you suggest for this?
Here is the simplified version of my code (I'm sure it is a bad design, because the store is not pure anymore, it was adjusted to these specific components)
var MovieStore = _.extend({}, EventEmitter.prototype, {
getAll: function() {
return movies;
},
//this will notify when the "search result" changed
emitListChange: function() {
this.emit('listChange');
},
//this will notify when the "typeahead string" changed
emitSearchChange: function() {
this.emit('searchChange');
},
// ... add/remove listeners
});
var Typeahead = React.createClass({
componentDidMount: function() {
MovieStore.addSearchChangeListener(this.onMoviesChanged);
},
onMoviesChanged() {
this.setState({
movies: Movies.getAll(),
});
},
//...
});
var List = React.createClass({
componentDidMount: function() {
MovieStore.addListChangeListener(this.onMoviesChanged);
},
onMoviesChanged() {
this.setState({
movies: Movies.getAll(),
});
},
//...
});
Why not have each component simply listen for a CHANGE event from the store. Then each component can query the store for the information it needs.
Example...
var Typeahead = React.createClass({
// deliberately omitting some stuff
componentDidMount() {
MovieStore.addChangeListener(this.onMovieStoreChange);
},
onMovieStoreChange() {
this.setState({movies: MovieStore.getAll()});
},
});
// Then in List
var List = React.createClass({
componentDidMount() {
MovieStore.addChangeListener(this.onMovieStoreChanged)
},
});
So, now, whenever the MovieStore updates, both the Typeahead and the List will update. Don't worry about the "cost" of updating both components when perhaps only one needs to be updated: it's miniscule.
Hope that helps
I am writing a authentication module in Flux, actions : auth,auth_success,auth_error. I am thinking when action auth_error occur, the router will go to '/login'. When action, action, auth_success occur, the router will go to '/dashboard'.
But it seems to be wrong because action only goes to dispatcher. I don't know how to do route the callbacks. Maybe check the store value?
You have to mixin your React class with Route.Navigation object, for instace
/** #jsx React.DOM */
var React = require('react');
var Router = require('react-router')
, Navigation = Router.Navigation;
var UserStore = require('user-store');
var YourClass = module.exports = React.createClass({
mixins:[Navigation], //This is very important
getInitialState: function() {
return {};
},
componentWillMount: function(){
UserStore.addChangeListener(this._method);
},
componentWillUnmount: function(){
UserStore.removeChangeListener(this._method);
},
render: function() {
return (
<div>
</div>
);
},
_method: function() {
// From here you can call methods of Navigator Object
this.transitionTo('SomeRouteName'); //This method will render the specified <Route>
}
});
For further information you can check
https://github.com/rackt/react-router/blob/master/docs/api/mixins/Navigation.md
In order to change the route and according to flux architecture, you should call transitionTo from a callback of some User Store you should have.
I added an example to the code, you may customise it to your specific case.
Happy coding!