-
Notifications
You must be signed in to change notification settings - Fork 594
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
Prevent ConnectedRouter from re-rendering on every redux store update #208
Conversation
Nice eye @AnnaTsu . Judging from the code, there doesn't seem to be any downside to having |
@jakewies, everything is simple, any change in the redux tree triggers calls of all |
Been running a similar version all day (#204 (comment)), does not seem to have any issues with having a shouldComponentUpdate method (Or PureComponent). |
It does exactly what @firec0der said. It isn't checking for prop changes, and this is why it keeps re-rendering. |
@madsmadsen So you aren't having extra renders while implementing |
Rerenders stopped from ConnectedRouter when |
Love this! @AnnaTsu, would you mind add a unit test to prevent this bug in the future? |
I'll add it as soon as i get home, @supasate :) |
Hey! I have written two tests. One to check if the component is rendered only once when mounted, and the other to check if it does not re-render when an action that has nothing to do with the history or router is fired. However, I also tested modifying the I'm not very familiar with testing React components, so maybe this can be the problem. Can someone see if there's something wrong in the tests? |
@AnnaTsu It's a difficult one to test, you'll need to have the component rendered inside the Route to be connected to the store, probably subscribed to a property in the store. Update a different property in the store, and see if it triggers a rerender. I do not currently have time to look more into it, but I might have time later this week. |
@madsmadsen I believe that's what I've done in this test. However, I couldn't reproduce the same behavior when testing in this POC.
|
store.dispatch({ type: 'testAction' }) | ||
history.push('/new-location') | ||
expect(renderCount).toBe(2) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AnnaTsu Just so I understand what's going on here, store.dispatch()
will not re-render RenderCounter
, BUT history.push()
will, correct? Therefore renderCount
should be 2.
initial render + history.push() = 2 renders
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly. Initial render times upon mount should be one.
Then, i fire an action that has no connection to what ConnectedRouter
should listen to, and modify the history (something that the component should re-render).
So, the expected render times should be 2.
@AnnaTsu could you explain how you went about testing this? Was it programmatically inside a test file or were you testing it in a consuming application, such as the POC? Reason I'm asking is because I pulled down this branch and ran EDIT: Nevermind this, I'm just testing programmatically now. I can see that changing the implementation of
is giving false positives because tests are passing. I'll continue to look into this. |
Yes, @jakewies. I tested the render times via the POC. By that, i saw that changing the However, the tests are passing even when |
Right, I'm seeing that now. The assumption is: If However, in your test the store.dispatch({ type: 'testAction' }) This goes against our observations in the POC. I'm not sure why this is happening. |
Is there a possibility that My hypothesis is that there is a distinct difference between the test environment and the POC, and that difference is what's causing the confusion and could lead to the actual solution. I'm just spitballing here. |
Re-renders come from dispatching a location change action for the initial location As result I think the solution is add the meta info to onLocationChange action to describe is initial action or not. And don't handle initial |
Hi, what's the ETA on this being released? I upgraded react-redux-forms as they had a bug in the old version, and long story short it meant upgrading most packages incl. this. Because of all the dispatches from redux forms the page starts re-rending constantly, but to make it worse because the page reloads the dispatches happen again and .... infinite loop I don't know if you have any work around for this, but I'd be happy to use a beta version if there's plans to release this change asap? |
@jakewies Your error with yarn link may be caused by using different ReactReduxContext. The library itself uses ReactReduxContext from its local node_modules directory, while, the POC app uses ReactReduxContext from the POC app's node_modules directory. Let's try passing the same ReactReduxContext imported directly from 'react-redux' (or a new context) to both Provider and ConnectedRouter. |
@SergeyPoluektov the unit test in this PR also uses plain JS structure and it passes even without PureComponent. |
@gitn00b1337 We should find the root cause first. So, we can fix at the right point. Otherwise, the implemented unit tests will be nothing and it may break again in the future. I have no clue now about this behavior and will spend more time looking into it this weekend if it's still unresolved. Eventually, if we still cannot understand the root cause (maybe within a week from now), we may need to release a hot fix first and make it right later. However, I'd like to spend a bit more time to find the root cause first before we go to this route. |
Ok thanks for getting back to me, if you could post here when something is released I'd appreciate it greatly, thanks guys |
First of all, this unit tests don't work correctly for a case when redux store updates by non-related action. Try this one render-test After that, tell me please, what the reason behind passing |
Thanks for asking. It would be useless now. Seems like I just forgot to remove it in this commit when I migrated to a new react-router version. |
@supasate so, removing this |
@SergeyPoluektov I noticed some differences in your gist compared to the current test file, mainly your use of I'm curious to know your thought process behind this. Is this a meaningful change, or can the two be used interchangeably in tests? |
@jakewies no, it's not meaningful change. Both of them can be used interchangeably in tests. Basically, differences in my gist compared to current files are about using non-mocked redux |
@SergeyPoluektov got it. Thanks for the explanation 👍 |
I'm going to test the gist out in a few. Based on @SergeyPoluektov insights it looks like the recommended changes are:
Am I missing anything? |
@jakewies one more improvement regarding immutable selectors: |
@supasate i am seeing something a bit weird in the reducer:21 when you try to merge the two objects together. |
Any updates? |
Hi all. Thanks for your insights and patience! For this PR, let's keep it simple by focusing on fixing double rendering in general case first (exclude the initial LOCATION_CHANGE and the infinite loop problem). I'll fix other issues in separate PRs. So, @AnnaTsu, the
I tested in #215 and it works well for plain, immutable, and seamless-immutable structures. |
Sure! Gonna do it in a moment 😄 I've had a long time without coming back to Github, it's good to come back and see the problem being solved by the community. |
Applied the changes you commented and now all tests are indeed passing 😄 Now we can change the Good job, everyone 🎉 |
Can confirm that the |
LGTM! |
Thanks everyone!!! |
Can you release a minor version with this fix? |
@Arrvi I'm fixing other issues related to rendering too. Will release it soon. Hang tight! |
Released in v6.1.0 and Happy New Year 🎉! |
As discussed in #205 the children tree of
ConnectedRouter
component was re-rendering on every update from the redux store.I tried out @jakewies`s example repository and think that the problem is with the shallow compare of props coming from Redux.
This PR replaces the plain
Component
fromConnectedRouter
for aPureComponent
, which includes shallow compare of props, and prevents re-renders if they're equal.