Instantly share code, notes, and snippets.

Embed
What would you like to do?
Example for loading new external data in response to updated props
// 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 (prevState.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 });
}
});
}
}
@MrToph

This comment has been minimized.

Show comment
Hide comment
@MrToph

MrToph Mar 28, 2018

I think it's a good idea to link to this React blog post here for all people coming from the latest 16.3 blog post.

There's an alternative there to tracking the mounted state with making the ES6 promise "cancelable".

MrToph commented Mar 28, 2018

I think it's a good idea to link to this React blog post here for all people coming from the latest 16.3 blog post.

There's an alternative there to tracking the mounted state with making the ES6 promise "cancelable".

@ericketts

This comment has been minimized.

Show comment
Hide comment
@ericketts

ericketts Mar 28, 2018

is there meant to be a return here?

ericketts commented Mar 28, 2018

is there meant to be a return here?

@TimoSta

This comment has been minimized.

Show comment
Hide comment
@TimoSta

TimoSta Mar 28, 2018

@bvaughn Out of curiosity, why is getDerivedStateFromProps static?

Wouldn't it be nice to not having to put prevId into the state in this example, and instead checking it like this?

if (nextProps.id !== this.props.prevId) {

TimoSta commented Mar 28, 2018

@bvaughn Out of curiosity, why is getDerivedStateFromProps static?

Wouldn't it be nice to not having to put prevId into the state in this example, and instead checking it like this?

if (nextProps.id !== this.props.prevId) {
@gouegd

This comment has been minimized.

Show comment
Hide comment
@gouegd

gouegd Mar 28, 2018

@TimoSta this is explained here, search for "You may wonder why we don’t just pass previous props as a parameter to getDerivedStateFromProps"

gouegd commented Mar 28, 2018

@TimoSta this is explained here, search for "You may wonder why we don’t just pass previous props as a parameter to getDerivedStateFromProps"

@lifeiscontent

This comment has been minimized.

Show comment
Hide comment
@chipit24

This comment has been minimized.

Show comment
Hide comment
@chipit24

chipit24 Mar 31, 2018

@gouegd that doesn't answer @TimoSta's question. Do you understand what static methods are? :p

That piece from the blog post provides some justification as to why prevProps isn't passed as a parameter to the static method, but it doesn't explain why they decided to make the lifecycle method static.

Doing some Googling, I came across the RFC for static lifecycle methods and the following justification was provided:

Making certain lifecycles static to prevent unsafe access of instance properties.

Like the blog post mentions, by "unsafe" they probably mean "more likely to have bugs in future versions of React, especially once async rendering is enabled".

chipit24 commented Mar 31, 2018

@gouegd that doesn't answer @TimoSta's question. Do you understand what static methods are? :p

That piece from the blog post provides some justification as to why prevProps isn't passed as a parameter to the static method, but it doesn't explain why they decided to make the lifecycle method static.

Doing some Googling, I came across the RFC for static lifecycle methods and the following justification was provided:

Making certain lifecycles static to prevent unsafe access of instance properties.

Like the blog post mentions, by "unsafe" they probably mean "more likely to have bugs in future versions of React, especially once async rendering is enabled".

@inomdzhon

This comment has been minimized.

Show comment
Hide comment
@inomdzhon

inomdzhon Apr 1, 2018

Sure, @lifeiscontent is right, we missed this here

inomdzhon commented Apr 1, 2018

Sure, @lifeiscontent is right, we missed this here

@Stephen-ONeil

This comment has been minimized.

Show comment
Hide comment
@Stephen-ONeil

Stephen-ONeil May 28, 2018

In my own process of moving away from UNSAFE_... methods, I wrote a component using a pattern that is, in practice, identical to this. While it successfully heads off the setState-on-unmounted warning, in my own testing it doesn't do anything about the underlying issue of potential/real memory leaks.

Could someone else following this pattern try putting their component in a situation where it has unmounted before the promise resolves, and then use a heap snapshot to look for instances of the component persisting in memory? Maybe I'm wrong about my component being an "identical" case to this (could be leaky for other reasons), or maybe we're all just suppressing a warning that exists to flag potential memory leaks.

Thanks!

Stephen-ONeil commented May 28, 2018

In my own process of moving away from UNSAFE_... methods, I wrote a component using a pattern that is, in practice, identical to this. While it successfully heads off the setState-on-unmounted warning, in my own testing it doesn't do anything about the underlying issue of potential/real memory leaks.

Could someone else following this pattern try putting their component in a situation where it has unmounted before the promise resolves, and then use a heap snapshot to look for instances of the component persisting in memory? Maybe I'm wrong about my component being an "identical" case to this (could be leaky for other reasons), or maybe we're all just suppressing a warning that exists to flag potential memory leaks.

Thanks!

@Stephen-ONeil

This comment has been minimized.

Show comment
Hide comment
@Stephen-ONeil

Stephen-ONeil May 28, 2018

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.

Stephen-ONeil commented May 28, 2018

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.

@franklixuefei

This comment has been minimized.

Show comment
Hide comment
@franklixuefei

franklixuefei May 30, 2018

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.

franklixuefei commented May 30, 2018

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.

@khanhlu2013

This comment has been minimized.

Show comment
Hide comment
@khanhlu2013

khanhlu2013 Jul 18, 2018

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);
  }
}

khanhlu2013 commented Jul 18, 2018

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);
  }
}
@akonsu

This comment has been minimized.

Show comment
Hide comment
@akonsu

akonsu Jul 25, 2018

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);
    }
  }

akonsu commented Jul 25, 2018

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);
    }
  }
@Nico142

This comment has been minimized.

Show comment
Hide comment
@Nico142

Nico142 Aug 23, 2018

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?

Nico142 commented Aug 23, 2018

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment