What is the correct pattern in React JS to invoke a component method on specific props change?
If you want to run some code (e.g. data fetching) when props change, do it in componentDidUpdate
.
componentDidUpdate(prevProps) { if (prevProps.id !== this.props.id) { this.fetchData(); }}
In your example, this won't work because shouldComponentUpdate
returns false
. I'd argue this is not a very common case because typically you still want to re-render if props change.
For example, if the user ID changes, you might want to show a loading indicator while the data for the new user is loading. So avoiding a re-render is not very useful in this case.
However, if you're absolutely sure you both need to prevent a re-render and need to perform a side effect like fetching data on props change, you can split your component in two. The outer component would do the data fetching in componentDidUpdate
, and return <InnerComponent {...this.props} />
. The inner component would have a shouldComponentUpdate
implementation that prevents re-rendering further. Again, I wouldn't expect this to be a common scenario, but you can do this.
Based on the React docs and this discussion on github, The place to fetch new data based on props change is actually componentDidUpdate
.
The rendering was actually splitted to two phases, the Render Phase
which is pure and creates no side effects and the Commit Phase
which can run side effects, work with the DOM and schedule updates.
You can see that explained well in Dan Abramov's diagram:
Dan also mentioned:
People used to mix these two different things in componentWillReceiveProps, which is why we have to split it into a pure method (getDerivedStateFromProps) and an existing impure one where it’s okay to do side effects (componentDidUpdate).
And for the solution itself, Im attaching the example from the docs:
Fetching external data when props change
Here is an example of a component that fetches external data based on props values:
Before:
componentDidMount() { this._loadAsyncData(this.props.id);}componentWillReceiveProps(nextProps) { if (nextProps.id !== this.props.id) { this.setState({externalData: null}); this._loadAsyncData(nextProps.id); }}
After:
static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.id !== prevState.prevId) { return { externalData: null, prevId: nextProps.id, }; } return null; } componentDidMount() { this._loadAsyncData(this.props.id); } componentDidUpdate(prevProps, prevState) { if (this.state.externalData === null) { this._loadAsyncData(this.props.id); } }