-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
mapStateToProps called when component not due to render #1226
Comments
We still ensure top-down update ordering. It is one of our biggest challenges with this library. AFAIK, we didn't change this behavior in 7.0. Can you provide an example of what you're seeing in a Codesandbox? We need a reproducible test case to work with in order to figure out what's going on here. |
I can confirm we're having the same issue (effectively breaking our application when upgrading from v6 to v7) Here's an simple example (mind the typo's):
In this example, if shouldRender becomes true, it mounts MyBaseComponent. When for some reason the state is cleared (new api request), it unmounts again (state.page === null). However the last connect still receives a state update (state.page has changed) even though it's about to get unmounted. Even if we would guard state.page.title, it still would be a performance hit as all child components would recalculate a new value (think of custom mappers of data on every child component) We have a HOC that prevents mounting the component if certain data is not in the state (page not loaded). The next HOC (the connect) uses that data to display some information. The reason why we approach it this way, is that we can block a complex panel with many child components (with their own connects) from mounting, so the selectors used in the connects of the children don't break. I haven't resolved the issue. |
@Andrekra : as Tim said, it would really help if you can provide a project that reproduces the issue, preferably as a CodeSandbox. |
I also encountered this problem after upgrading to https://codesandbox.io/s/p9mp9rqq4m At first render And this is the right behaviour. When app renders you'll se in console
And an async action was dispatched to set
As you can see Also if I set the async action somewhere bellow 100ms, |
@markerikson Here's an example https://codesandbox.io/s/v629pkllj0 |
@markerikson I haven't debugged this yet, but just going from the code could the issue be in this line? I think what might be happening is that the |
Alright, Also, this test succeeds if we render the child from the start and only hide it. It has to be first hidden, then shown, then hidden again. Therefore I think this is what is happening:
I don't know if this is a react issue to be honest, since it feels like this should not happen. it('should not notify nested components after they are unmounted', () => {
@connect(state => ({ count: state }))
class Parent extends Component {
render() {
return this.props.count === 1 ? <Child /> : null
}
}
const mapStateToProps = jest.fn(state => ({ count: state }))
@connect(mapStateToProps)
class Child extends Component {
render() {
return <div>{this.props.count}</div>
}
}
const store = createStore((state = 0, action) =>
action.type === 'INC' ? state + 1 : state
)
rtl.render(
<ProviderMock store={store}>
<Parent />
</ProviderMock>
)
expect(mapStateToProps).toHaveBeenCalledTimes(0)
store.dispatch({ type: 'INC' }) // works with this: rtl.act(() => { store.dispatch({ type: 'INC' }) })
expect(mapStateToProps).toHaveBeenCalledTimes(1)
store.dispatch({ type: 'INC' })
expect(mapStateToProps).toHaveBeenCalledTimes(1)
}) |
My analysis above was wrong. Scratch all of the above. It is actually a different timing issue. The problem is that the store subscription is created inside
You can see in this sandbox how this happens with just plain react. The second The proper fix (that passes all tests including the one I posted above) is to use |
Yeah, I wondered whether having the store subscription in an async effect was going to cause issues. |
We are also experiencing the same issue with 7.0.2. I've tried to re-create with a minimal example in codesandbox, but I cannot get the same error. I'll keep trying. |
Do any of you that experience this issue use concurrent mode? @justincrow do you have exactly the same issue, i.e. a non-rendered component's mapState is called? I want to clarify whether this is still a timing issue with unmounting components or something different. |
No With new patch release I still encounter the issue. And the problem it seems to be as I said above
|
Hi @MrWolfZ , |
@iamandrewluca I had a look at your sandbox and the issue is something completely different, since in your case the You have already pointed out a workaround for your sandbox and in mine the workaround would be to simply check if |
@justincrow my current theory would be that something like this happens:
|
If anyone's still seeing this issue, we really need an example project that reproduces the problem. |
I've finally managed to re-create the error in codesandbox. |
@justincrow : cool, thanks. Can you point to specifically which files are showing the issue, and the exact steps to run in the sandbox to demonstrate that? |
When the sandbox runs, you'll see two links, 'Jim' and 'Bob', when you click on them, an initial action is dispatched to clear the id (which is checked to see whether to display the customer dashboard tab), then a timeout is set (to represent network activity) which sets the selected id. |
I made the sandbox much smaller, so that it is easier to analyze. I need to go to sleep now, and I will only be able to continue on Friday, but maybe somebody else can solve this. Based on my reduced example the issue can only be due to either the timing of the async fetch action in the select inbox thunk or due to a state update occuring inside |
I have further minimized the sandbox and the problem seems clear now: in certain situations two dispatches just after another will lead to |
I have created a new issue for this that contains a truly minimal reproduction sandbox. I will shortly create a PR with the repro as a test case and a potential solution. |
@justincrow 7.0.3 was just released and your sandbox works correctly with that version. Could you please test it in your app as well and see if there are still issues? |
@MrWolfZ Great work - this issue is resolved in our app. |
Hi,
I have a conditionally displayed component, based on a value in redux. All components below are
connect
ed.eg in a wrapper component's render method:
{ (showValueInRedux) ? <ComponentUsingShowValue/> : <EmptyComponent/> }
ComponentUsingShowValue
has amapStateToProps
that uses redux state that it expects to be there, hence we guard for that withshowValueInRedux
in the wrapper component.In version 6,
mapStateToProps
inComponentUsingShowValue
is only called when that component is due to render.In version 7,
mapStateToProps
is called even if the wrapping component has switched to choosingEmptyComponent
due toshowValueInRedux
being false (after having once been true).This causes an exception in our current code because the redux values that
mapStateToProps
uses ( in the now non-rendered component) are not available.Please could you clarify this change in behaviour?
The text was updated successfully, but these errors were encountered: