Skip to content
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

Breaking change in v7 - componentDidUpdate order #1432

Closed
macobo opened this issue Oct 21, 2019 · 8 comments
Closed

Breaking change in v7 - componentDidUpdate order #1432

macobo opened this issue Oct 21, 2019 · 8 comments

Comments

@macobo
Copy link

macobo commented Oct 21, 2019

We are running into some difficulty updating to v7 due to what seems like a breaking change.

Context: We're rendering react inside a larger non-react webapp, where when user does some action, some hidden forms get rendered and the form submit.

We detect if it's time to submit by using componentDidUpdate method on a parent component - relying that child forms have re-rendered by the time componentDidUpdate has been called.

With v7 this seems to be broken - componentDidUpdate for parent is called before child render.

Repro: https://codesandbox.io/embed/eloquent-bas-b12b6

V7 order:
image

V6 order:
image

This seems like a bug to us as official docs state

componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated. This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).

Is the order in v7 intentional?

@markerikson
Copy link
Contributor

We aren't using componentDidUpdate in v7, as connect is now implemented with a function component and hooks instead of a class.

Now, we are using useLayoutEffect to propagate change notices, which is supposed to run in the same phase as componentDidUpdate, but it's certainly possible there are timing differences.

React-Redux has never explicitly guaranteed the kind of behavior you're describing - that falls under the heading of implementation details, and isn't part of our public API.

@macobo
Copy link
Author

macobo commented Oct 21, 2019

Hi mark, thanks for the quick answer!

Is there a way to know once all changes have been processed and components re-rendered following an action being dispatched?

@markerikson
Copy link
Contributor

Unfortunately, not that I know of. I wish there was - it would be helpful for some of our benchmarking efforts.

@macobo
Copy link
Author

macobo commented Oct 21, 2019

I guess we have no other choice but to try remove our dependency on this behavior going forward, though I'm really surprised by this. Thanks for the help regardless!

@markerikson
Copy link
Contributor

Yeah, it's amazing how many subtle aspects of behavior people rely on and assume that it's "correct", even though it's actually implementation-specific. For example, in v6 we switched to passing store state via context for consistency, only to find out that people were relying on the immediate changes being reflected in child components in earlier versions when mounting code-split reducers via lazy-loaded components.

@markerikson
Copy link
Contributor

Actually, looking at the lifecycle trace, I think the other issue may specifically be with v6 versus other versions.

In v5 and v7, we implement our own store update cascading logic in order to enforce top-down updates. Connected ancestors notify nested connected descendants after the ancestors have finished rendering. This does actually mean that parent components are likely happening in a separate render cycle from the descendants.

In v6, everything was done via React's normal top-down rendering pass.

Thinking about it, that does likely mean that a parent will have completely rendered and be done, before a nested connected component renders.

@ckedar
Copy link

ckedar commented Nov 8, 2019

The root cause of this error is wrapping the ConnectFunction in React.memo, which makes the connect HOC a PureComponent which it should not be.

For now, as a workaround, pass {pure:false} as fourth param to connect for child.

@markerikson
Copy link
Contributor

@ckedar : no, that's not correct, and I don't know why you're saying that.

connect has always behaved as a PureComponent equivalent, but with a stronger net guarantee: if the combination of const mergedProps = {...ownProps, ...stateProps, ...dispatchProps} hasn't changed, then your own component won't render.

The React.memo() usage in v7 is a key part of that implementation, and is necessary for acceptable performance.

@timdorr timdorr closed this as completed Nov 22, 2019
@reduxjs reduxjs deleted a comment from ddt313 Jun 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants