-
-
Save bvaughn/982ab689a41097237f6e9860db7ca8d6 to your computer and use it in GitHub Desktop.
| // This is an example of how to fetch external data in response to updated props, | |
| // If you are using an async mechanism that does not support cancellation (e.g. a Promise). | |
| class ExampleComponent extends React.Component { | |
| _currentId = null; | |
| state = { | |
| externalData: null | |
| }; | |
| static getDerivedStateFromProps(nextProps, prevState) { | |
| // Store prevId in state so we can compare when props change. | |
| // Clear out previously-loaded data (so we don't render stale stuff). | |
| if (nextProps.id !== prevState.prevId) { | |
| return { | |
| externalData: null, | |
| prevId: nextProps.id | |
| }; | |
| } | |
| // No state update necessary | |
| return null; | |
| } | |
| componentDidMount() { | |
| this._loadAsyncData(this.props.id); | |
| } | |
| componentDidUpdate(prevProps, prevState) { | |
| if (this.state.externalData === null) { | |
| this._loadAsyncData(this.props.id); | |
| } | |
| } | |
| componentWillUnmount() { | |
| // Prevent potential setState calls after unmount, | |
| // (Since these trigger DEV warnigs) | |
| _currentId = null; | |
| } | |
| render() { | |
| if (this.state.externalData === null) { | |
| // Render loading state ... | |
| } else { | |
| // Render real UI ... | |
| } | |
| } | |
| _loadAsyncData(id) { | |
| if (id === this._currentId) { | |
| // Data for this id is already loading | |
| } | |
| this._currentId = id; | |
| asyncLoadData(id).then(externalData => { | |
| // Only update state if the Promise that has just resolved, | |
| // Reflects the most recently requested external data. | |
| if (id === this._currentId) { | |
| this.setState({ externalData }); | |
| } | |
| }); | |
| } | |
| } |
Part of the reason of getDerivedStateFromProps() being static is that it enforces that this function stays pure, because a static method has no access to this, thus mutations like this.setState() are not possible.
What about if we remove static getDerivedStateFromProps method all together, and update componentDidUpdate as follow:
componentDidUpdate(prevProps){
if(this.props.id !== prevProps.id){
this.setState({externalData:null});
this._loadAsyncData(this.props.id);
}
}
A bug:
componentDidUpdate(prevProps, prevState) {
if (prevState.externalData === null) {
this._loadAsyncData(this.props.id);
}
}
must read
componentDidUpdate(prevProps, prevState) {
if (this.state.externalData === null) {
this._loadAsyncData(this.props.id);
}
}
I thought the same as @khanhlu2013. Is there a reason why you used getDerivedStateFromProps instead of comparing the IDs (this.props.id !== prevProps.id) in componentDidUpdate?
shouldn't asyncLoadData catch error?
asyncLoadData(id).then(externalData => {
// Only update state if the Promise that has just resolved,
// Reflects the most recently requested external data.
if (id === this._currentId) {
this.setState({ externalData });
}
});
should be
asyncLoadData(id).then(externalData => {
// Only update state if the Promise that has just resolved,
// Reflects the most recently requested external data.
if (id === this._currentId) {
this.setState({ externalData });
}
}).catch(() => this._currentId = null);
akonsu's change is required.
inomdzhon's change is required.
If the author isn't going to update the gist, could someone else create a new one, along with a online running example?
@Stephen-ONeil I feel the same way, if (id === this._currentId) { is basically just an isMounted check ...
A bug:
componentDidUpdate(prevProps, prevState) { if (prevState.externalData === null) { this._loadAsyncData(this.props.id); } }must read
componentDidUpdate(prevProps, prevState) { if (this.state.externalData === null) { this._loadAsyncData(this.props.id); } }
Yes, This will cause the second render to fail. Because prevState is having old data now.
Blog post have the correct code
Thanks for pointing out the typo. I often don't notice comments on Gists. I've updated the example.
To put it another way, I feel like we both effectively re-invented isMounted(), meaning patterns like this have the same pitfalls laid out in the first couple paragraphs of this post.