-
Notifications
You must be signed in to change notification settings - Fork 46.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The minor release of 16.4 causes BREAKING changes in getDerivedStateFromProps #12898
Comments
Update: see my comment at the bottom of this thread for the conclusion. TLDR: if this change broke your code when you moved from 16.3 to 16.4, your code was already buggy in subtle ways. The change in React 16.4 made these bugs in your product code occur more often so you can fix them. We don’t consider making existing bugs in your code reproduce more reliably to be a breaking change. If you're coming from an issue in some third-party library that works with 16.4, but doesn't work in 16.3 — you're hitting the React bug. This issue is a complaint about this bug getting fixed in 16.4. However, we think fixing it was the right choice. We recommend everyone to upgrade to React 16.4 or later. Again, for details, walkthroughs and demos, see my comment at the bottom of this thread. I’m sorry this broke things for you. Can you show the relevant code please? We’re aware this change can cause issues in rare cases, as documented in the blog post. Unfortunately not making this change also leads to buggy behavior, although less obvious and deterministic so maybe you didn’t bump into it yet. I can see your point that fixing it is “breaking” in your case, but I hope you can also see that fixing any bug can be considered breaking for the code that relied on it. If your code relied on the old behavior but doesn’t work with the new one, it worked by accident. It’s our fault for overlooking this in the 16.3 release but it took about a month for us to discover this problem from bug reports at Facebook. The function takes two arguments: props and state. But it only runs when one of them updates. This didn’t really make sense and was an oversight in the initial implementation. When we fixed that issue at Facebook (which, as we learned, was the reason behind numerous bugs), one component that relied on the buggy behavior broke (just like in your case). This was just one component out of a thousand or so. We decided that it’s better in the long term to make the fix than to allow more components to be written that rely on the bug. Again, I’m sorry this caused issues for you. Seeing your code would be helpful. |
The documentation on the previous blog post seems to completely contradict the changes made in v16.4. Perhaps a note should be added to prevent confusion? |
@chase Can you be more specific as to where you see the contradiction? It fires more often but the examples in the post still work the same way. |
@gaearon can you expand on this? The spec'd behavior in the RFC never mentioned this, so it seems like it was an issue with the initial API design and not it's implementation? |
yeah i was also surprised here, the old behavior was more waht i'd have guessed the behavior would be from reading through the RFC |
The RFC does say:
We didn't want to over-specify when exactly this happens because there might be multiple reasons. Again, the new behavior is critical to making None of the intended usage examples we provided in RFCs, blog posts, or docs would break with the new behavior. |
I'm not sure I understand the benefit or reasoning to have this method called on every single change. We had to go back and write much more logic to handle the internals of all the The 16.3.2 version acted more in lines with the former The blog post provided by @chase states:
This is exactly what we expected and how it operated in 16.3. That is much cleaner in my opinion. As a side note, and on a personal level, I never liked the deprecation of |
If we want to continue this discussion let's start talking about specific code examples. |
(That was in response to a typo, now edited) |
Also I'd like to ask everyone to keep in mind that we're not doing changes just to piss people off. We want what is best for apps in longer term, and we care about not adding extra boilerplate as deeply as you do. Again, we're sorry for the churn this caused. We'll make another blog post about this next week. It seems like more people use But to continue this discussion we need code examples. You can see otherwise it immediately turns abstract and vague. |
@gaearon No, no confusion, just poor copy/pasting. I immediately edited it. Thanks. |
Could you post an example of the code that got broken by the new behavior? |
Here is the simplest example I can provide from our morning of refactoring to support the update. Before: (only called on prop changes)
After: (refactored because it is called every change)
At this point, the This specific example is small and I can see the "no big deal" attitude that some people may give based on it but again, we have much larger refactors that this change caused. |
I believe the “before” code already had a bug (so 16.4 just surfaced it).
It may be true that in your particular case this prop never changes. That’s a pretty fragile assumption from the component’s point of view—ideally it should be able to work with new props—but maybe it’s a reasonable intentional limitation of this component’s API. Still, its parent component could re-render at any time (e.g. if it had It’s hard to tell whether this can happen or not without checking the source of every single component above in the tree. So even if this doesn’t happen right now in your code, the chances are high it might happen in the future when you’re working on a completely unrelated feature. So why is that a problem? If any of the parents re-render, This is why the “before” code already had a bug, and the change in React 16.4 helped uncover it. Even if none of the components above currently ever re-render, that’s an implicit assumption that makes every component with That's exactly the kind of broken assumption that led to bugs at Facebook, and led us to do this change. React 16.4 calls it more often which surfaces such bugs that already exist in your code. Conversely, Note how in the 16.3 blog post we explicitly demonstrated you need to keep previous values in the state for use cases like this, just like you ended up doing in the “after” example. Then you wouldn't have this problem now. Note that additionally, Finally, there are also more subtle issues that would happen in async mode with code like this. Since the whole point of |
For what it's worth, none of my code broke after the update. It was specifically based off of this example on the blog. Based on the changes mentioned in the newest post, I had assumed that this would no longer behave as expected: 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;
} |
I don’t see why that example would break with the update. Am I missing something? As far as I can tell the conditional check would be false on state-only updates, and it would return null. |
Why I still have an error: http://i.imgur.com/fjFSDgq.png , when I do not have any |
@RoyalHunt Please don’t use this thread to discuss a completely unrelated problem. If you believe it’s a bug please file a new issue with a reproducing example. |
I suppose the fact it looked so similar to what the blog post states breaks is what threw me off: static getDerivedStateFromProps(props, state) {
if (props.value !== state.controlledValue) {
return {
// Since this method fires on both props and state changes, local updates
// to the controlled value will be ignored, because the props version
// always overrides it. Oops!
controlledValue: props.value,
};
}
return null;
} The similarity is superficial: static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.id !== prevState.prevId) {
return {
// Since this method fires on both props and state changes, local updates
// to the controlled value will be ignored, because the props version
// always overrides it. Oops!
prevId: nextProps.id,
};
}
return null;
} |
The given explanation for why the change was made is reasonable, it seems, but that's a separate issue from the fact this change was released without a major semver bump. 16.4 breaks what was generally understood to be the correct behavior in 16.3. I hear that there was some intentional ambiguity in the spec, but that doesn't change how folks understood I'm glad none of my own code broke, but for a project as widely adopted as React it seems reckless to ship a breaking change under the guise of a "bugfix." I understand it may be inconvenient and out-of-step and bad marketing to release a new major version all of a sudden, but on the flipside this makes React's versioning less reliable and it makes me a bit more hesitant about upgrading to new React versions. |
Actually I have some breaking problems as well with using apollo 1. But I solved this with the next code: static getDerivedStateFromProps(nextProps, state) {
const savedArticles = nextProps.data.articles
if (savedArticles && !state.firstRender && savedArticles !== state.savedArticles) {
return {
savedArticles,
firstRender: true
}
}
return null
} Before it was: static getDerivedStateFromProps(nextProps) {
const savedArticles = nextProps.data.articles
return {
savedArticles,
firstRender: true
}
} |
@gaearon I don't have any code examples (and I've not even installed 16.4 yet) but I just wanted to comment on what you said earlier...
I'm surprised you're surprised about this. I fully expected people to use this method simply because core methods on which, I'm sure, many relied upon previously are being removed. Namely those that allow us to tell when props change externally. I, personally, got the impression that this is almost the new As for me, my use case for Before
|
Our original implementation was flawed. For what it's worth, the RFC spec for
And our examples/recipes recommended comparing new and previous prop values before updating state- but we failed to community this warning clearly enough in the API reference docs, and I apologize for that.
We need to do a better job of communicating when
We will be sure to include several examples in the upcoming update about this. |
@bvaughn Looking forward to the article. One thing I'd suggest, and it's a comment that a few of my colleagues have expressed - lots of the important documentation actually seems to be in blog posts and not the API pages themselves. I would personally prefer to see documentation expanded and blog posts just linking or quoting parts of it. As it stands, there are things (logic, suggestions, exceptions) which are only available in blog posts. |
That's a good point, @codeaid. It's can be difficult to strike the right balance between too much and too little detail, but I think our current reference section definitely needs some work. We hope to eventually create a "recipes" section on the site that shows common tasks and our suggested patterns. Unfortunately there are only a couple of us though, and this sort of thing is pretty time consuming. But I appreciate the feedback. We'll try to keep it in mind as we make small edits over the next couple of weeks. |
@bvaughn Ask Mark to give you more people! It's ridiculous that a library this popular globally is getting so few resources :) |
Assign Mark a few code reviews at least! |
Am I the only one who now just can't grasp the naming and the param names of this method? |
Would it help to say that arguments are just called I thought prefixes would be helpful but I see now they’re more confusing.
I don’t understand this question. The use cases haven’t changed. If you needed this method in 16.3 and your code doesn’t have bugs (such as the one described above), it should also work in 16.4. So if you “need it” it’s for the same reason you “needed it” in 16.3. If you don’t need it that’s cool too—it’s not intended to be commonly used. As I already noted a few times in this thread it’s hard to say whether you need it if you don’t show a particular snippet of code. |
No, you're not the only one, @dubbha! Andrew and I were talking about this earlier this week and decided to update the docs to just be Edit: Docs updated via reactjs/react.dev/pull/910 |
This writing will lead to an infinite loop, always asynchronous request, how to solve this problem? |
As explained both in API reference for componentDidUpdate(prevProps, prevState) {
if (prevProps.something !== this.props.something) {
// do something
}
if (prevState.somethingElse !== this.state.somethingElse) {
// do something else
}
} |
I have a piece of code, as follows:
How do I change it with getDerivedStateFromProps?(I tried using ‘getDerivedStateFromProps’ and ‘componentDidUpdate’, but it went into an infinite loop.I am very annoyed) |
Please show what you tried. |
I have solved this problem, thank you anyway. |
I'm still a bit perplexed on this myself. While I understand the idea of "controlled" and "uncontrolled" data, I've got a few places where I want it to be both. I have two examples (both have the same code, one using React 16.3 and the other React 16.4) with two intentions:
React 16.3: https://codesandbox.io/s/9lopjp809o In both intentions I want to make a copy of the external prop value and save it internally. Equally, I want any external changes to overwrite the internal state. The I don't doubt that there's probably an anti-pattern to the React 16.3 |
@gaearon I did, but still wanting that sweetness of the
But the problem that I'm finding is that I just don't want to compare both objects on each render (because when
I realise I've written a comment where I'm working stuff out at the same time. I'll modify my Codesandboxes to play around and get back to you... |
I don't think your use case is memoization? What you're trying to do seems to be covered here: Including why it's fragile and probably not a good idea. |
@gaearon Yeah, my approach is definitely not the right idea. However, just trying to figure out what the alternative is when wanting to sandbox some information that then may change due to other external side-effects. Thought it was still relevant to have something like |
Here's an updated example with 16.4 that works: https://codesandbox.io/s/m4km18q38j My changes (compared to the broken 16.4 version you posted are):
Hope this is helpful! |
@gaearon very helpful! Thanks for the update and the clear breakdown. Much appreciated. The use of the |
@gaearon is there anything the react team can do to warn users when they install 16.3.x? The newer gDSFP behaviour changes component fundamentals, for instance in the transition component in react-spring. Hence i get issues posted for it every now and then, for instance this one: pmndrs/react-spring#198 gDSFP is assuming it's going to be called by setState (which is used by transition when exit-out transitions are completed). As a direct result old transitions are stuck in limbo in 16.3.x until a new render pass begins. |
I don't think this is a serious enough bug to deprecate 16.3.x (which is the only way we can warn them). We try to use npm deprecations very sparingly — either for security vulnerabilities or for guaranteed crashes. Otherwise people won't treat them seriously. I'm sorry that a React bug is affecting your users. One thing you could to is to cut a major release and raise the minimal required React version to 16.4. Then at least it'll be clear to new users. |
Maybe |
@gaearon could i ask for one last thing? Your answer at the top makes it seem like when libs break that's on us, not react, since our code must've been buggy already. Could you please amend this? I'm going to warn users now to upgrade react and i'm going to link to this issue. The new behaviour (which im totally fine with) is definitively changing how react functions, and if any component assumes setState is going through gDSFP it's going to break in 16.3.x, and safeguarding against that will cost some major churn. A warning would be fine, but not when users come back and say "Dan Abramov said its your fault!" 😇 |
Updated to:
I think this should be clearer? |
nice, thanks! 🙂 |
I'm going to lock this thread since it's super large and everyone gets notified on new comments. |
According to semver, only non-breaking changes are supposed to go into minor and patch version releases. With the release of 16.4, you have made breaking changes to
getDerivedStateFromProps
. Our entire codebase, running perfectly on 16.3.2 is a dumpster fire as soon as we raise that dependency to 16.4.The only thing in your CHANGELOG about this breaking change is:
Please revert this change and save it for 17.0.0, or provide proper documentation to what this change actually entails so that its use can be adjusted by those who have already implemented
getDerivedStateFromProps
.The text was updated successfully, but these errors were encountered: