Let's say you have a Pet component displaying a pet resource. You also have an owner resource that belongs to pet via an ownerId attribute. Where in the Pet component should you load the owner resource?
const Pet = React.createClass({
getInitialState() {
return {
pet: PetStore.get(this.props.petId)
};
},
componentDidMount() {
PetStore.addChangeListener(this._onChange);
},
componentWillUnmount() {
PetStore.removeChangeListener(this._onChange);
},
render() {
var owner; // where should I initiate the ajax request to get owner?
return (
<p>{owner.name}</p>
);
},
onChange() {
this.setState({
pet: PetStore.get(this.props.petId)
});
}
});
As Thilo mentioned, the React documentation suggests that you do asynchronous loading of data in componentDidMount
. The reason for doing it on componentDidMount
instead of componentWillMount
is that componentWillMount
will get executed if you run React on the server for the initial page load, and you don't want to make Ajax calls on the server.
With that said, I would recommend that you move your data fetching logic to another component (often called view controller in the React world). This means that instead of having one component doing both data fetching and rendering, you have two components. One whos responsibility is to fetch data, and the other is to render that data. You'd pass that data as props to the rendering component from the view controller component. A small example would be:
var Pet = React.createClass({
render() {
return (
<p>Pet: {this.props.pet.name}</p>
<p>Owner: {this.props.owner.name}</p>
);
}
});
var PetController = React.createClass({
getInitialState() {
return {
pet: PetStore.get(this.props.petId),
owner: null
};
},
componentDidMount() {
OwnerStore.getOwner(this.state.pet.ownerId)
.then(owner => this.setState({owner: owner}));
},
render() {
return <Pet pet={this.state.pet} owner={this.state.owner} />;
}
});
What this separation gives you is simpler and more focused components. The Pet
component doesn't have to worry about where to get the data, and it can be reused in other cases where you get the data from some place other than the PetStore
. And the PetController
component focuses only on data fetching and state management. To make the Pet
component even simpler, you can avoid rendering it until the owner is fetched. Something like:
var PetController = React.createClass({
getInitialState() {
return {
pet: PetStore.get(this.props.petId),
owner: null
};
},
componentDidMount() {
OwnerStore.getOwner(this.state.pet.ownerId)
.then(owner => this.setState({owner: owner}));
},
render() {
if (this.state.pet && this.state.owner) {
return <Pet pet={this.state.pet} owner={this.state.owner} />;
} else {
return <div className="spinner" />;
}
}
});
Then your Pet
component doesn't have to worry about asynchronous data fetching because it won't get rendered until the data is there.