I'm writing a web app with react and I'm following the flux architecture. Sadly, all the tutorials I've seen so far either only use 1 component, or they have multiple components that are nested.
my question is about how to use multiple components, that aren't nested, together. For example - a list of items component working with some sort of a form component to add new items. Every example usually puts the form and the list in the same parent container.
in the example below, assuming the form and the list components each have their own action/store, how do I say when the form's submit event finishes firing, update the list component?
do I add a reference to the form component in the list component (like require('component')
?
is this bad practice? should I make a super component that has both of these components inside of it?
var List = React.createClass({
render: function() {
return(
//list of items of some sort
);
}
});
var Form = React.createClass({
handleSubmit: function() {
//handle submit by firing an action for the dispatcher
},
render: function() {
return(
//form
);
}
});
In most applications you'll need your stores to allow components to subscribe to changes, so that they can be notified when the state has been updated.
You can create an event handler for your store, then attach a listener in your list component.
var List = React.createClass({
getInitialState: function() {
items: []
},
componentWillMount: function() {
store.addChangeListener(this.updateItems);
},
updateItems: function() {
this.setState({ items: store.getItems() });
},
render: function() {
return (
// ... render this.state.items
);
}
});
This example uses setState
which causes the component to re-render, however you don't have to make your component stateful. You could also pass the data from the store down as a prop.
You can still dispatch the action from your form component without making any changes to that code, however your store must call the listeners whenever the data is changed.
Different Flux implementations handle this idea of notifying the store consumers in different ways.
Regular vanilla Flux is so unopinionated that it doesn't actually come with a system for listening to stores. It is suggested that you inherit from Node's EventEmitter class. In one example they create two new methods on the store that wrap around this functionality.
emitChange: function() {
this.emit(CHANGE_EVENT);
},
/**
* @param {function} callback
*/
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
}
Then when an action modifies the store, the handler for that action is responsible for calling the emitChange
function.
case TodoConstants.TODO_CREATE:
text = action.text.trim();
if (text !== '') {
create(text);
TodoStore.emitChange();
}
break;
Some Flux implementations such as Reflux make their stores behave in this way out of the box and the new state is passed as an argument to the listener function.
onStatusChange: function(status) {
this.setState({
currentStatus: status
});
},
componentDidMount: function() {
this.unsubscribe = statusStore.listen(this.onStatusChange);
},
Other, implementations such as Redux come with a wrapper layer for React, which allow you to use a more declarative approach and directly map some state from your store onto a component as properties.
// Which part of the Redux global state does our component want to receive as props?
function mapStateToProps(state) {
return {
items: state.items
}
}
var WrappedList = connect(mapStateToProps)(List)
// will receive items from redux state as a prop
<WrappedList />
When the state changes, the items part of the state will be passed as a property called items
and your component will re-render.