-
-
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
[FIX]: #1219 Save references before update #1220
Conversation
Deploy preview for react-redux-docs ready! Built with commit b2c4230 |
Yeah, this writeup matches my own mental model of what should happen, and what is probably happening. I'd love a test for this too, and would really appreciate it if someone can still try writing one. But, looking at this change, I'm fine with merging this one in now. Awesome work! |
I debugged the reproduction repo, and was able to figure out exactly when the bug happened. It appears that if you use There is a flag called However, when I tried dispatching an action from an event handler in the same setup, The library that was used in the reproduction repo ( My question now, what is the typical use-case scenario for using |
@MargaretKrutikova : nice detective work! yeah, that totally makes sense to me. Looking at the source code at Oh. OH!!!!! NOW I see what the real issue is! When you render
private _renderWithReactReduxContext = () => {
const { store } = this.props;
// store.getState is important here as we don't want to use storeState from the provided context
return (
<ReactReduxContext.Provider
value={{ store, storeState: store.getState() }}>
{this._renderChildren()}
</ReactReduxContext.Provider>
);
}; In v7, our context value looks like I've filed microsoft/redux-dynamic-modules#76 to bring this up. (paging @navneet-g ... ) |
Wow, what a turn :) Thanks for explaining it to me, @markerikson! So there is no need to come up with a test for this use-case, I assume. I wasn't able to replicate it in test in a clean way anyway. Since Good thing we figured the actual bug wasn't in |
Yeah, for a couple of the edge case tests we recently added, I actually had to not wrap the updates in |
Yeah :) It is a bit weird that I didn't see any warnings when I ran the tests though .. |
* Update React to latest * Update React peer dependency to 16.8.x * Initial re-implementation of `connectAdvanced` using hooks Matches changes from v7.0.0-alpha.1 * Update tests to match v7-alpha.1 behavior Added rtl.act() calls around dispatches and other component updates Added clarification on expected mapState calls in some places Disabled some no-longer-relevant tests per implementation Made tests run against React 16.8 by default * adding a react hooks test that fails with v7 alpha * wrapping store.dispatch with rlt.act, fixes component renders * reducing hooks test to 2 components * Fix case where wrapper props changed before store update render * Mark ReactDOM as global in UMD builds Matches state as of v7.0.0-alpha.2 * Fix perf problems with out-of-bounds array access Matches state as of v7.0.0-alpha.3 * Add modules to handle importing batchedUpdates * Use appropriate batched update API for subscriptions * Inject unstable_batchedUpdates in default entry point * Provide an alternate entry point for alternate renderers Matches state as of v7.0.0-alpha.4 * Remove batch arg from createListenerCollection (#1205) This prevents a bug with Terser (webpack's default minifier) where the returned batch function isn't defined due to function inlining. Matches state as of v7.0.0-alpha.5 * Remove older React versions from Travis * Add comments to connectAdvanced. Many much comments! * Re-add test for a custom store as a prop * Fix null pointer exception when store is given as a prop We were trying to read contextValue.subscription, even if that value was null. Reworked logic to handle cases where the store came in as a prop. * Ensure wrapper props are passed correctly when forwarding refs * Add a test to verify subscription passthrough with store-as-prop * add non-batched tests (#1208) * Force SSR tests to mimic a Node environment * Restructure connect tests to group by category for easier reading Yeah, this kills the blame history. Sorry. But it's a lot easier to figure out what the tests are used for now. * Clean up dead code in Provider tests * Add tests to verify errors are thrown for bad mapState functions * Fix edge cases around saved wrapper props and error handling Changed to useLayoutEffect to ensure that the lastWrapperProps ref is written to synchronously when a render is committed. However, because React warns about this in SSR, added a check to fall back to plain useEffect under Node so we don't print warnings. Also added logic to ensure that if an error is thrown during a mapState function, but the component is unmounted before it can render, that the error will still be thrown. This shouldn't happen given our top-down subscriptions, but this will help surface the problem in our tests if we ever break the top-down behavior. * Formatting * Add a test to verify no errors are printed in SSR usage * Ignore .idea/ * 7.0.0-beta.0 * Updated outdated SSR-test (dispatch in ancestors) (#1213) * Added test for injecting dynamic reducers on client and server (#1211) * Remove WebStorm gitignore This goes in a global gitignore file, not a project. * [FIX]: #1219 Save references before update (#1220) * Re-ignore .idea/ * 7.0.0-beta.1 * Update the codecov config to be more forgiving. * add test to verify that mapStateToProps is always called with latest store state (#1215)
* Update React to latest * Update React peer dependency to 16.8.x * Initial re-implementation of `connectAdvanced` using hooks Matches changes from v7.0.0-alpha.1 * Update tests to match v7-alpha.1 behavior Added rtl.act() calls around dispatches and other component updates Added clarification on expected mapState calls in some places Disabled some no-longer-relevant tests per implementation Made tests run against React 16.8 by default * adding a react hooks test that fails with v7 alpha * wrapping store.dispatch with rlt.act, fixes component renders * reducing hooks test to 2 components * Fix case where wrapper props changed before store update render * Mark ReactDOM as global in UMD builds Matches state as of v7.0.0-alpha.2 * Fix perf problems with out-of-bounds array access Matches state as of v7.0.0-alpha.3 * Add modules to handle importing batchedUpdates * Use appropriate batched update API for subscriptions * Inject unstable_batchedUpdates in default entry point * Provide an alternate entry point for alternate renderers Matches state as of v7.0.0-alpha.4 * Remove batch arg from createListenerCollection (reduxjs#1205) This prevents a bug with Terser (webpack's default minifier) where the returned batch function isn't defined due to function inlining. Matches state as of v7.0.0-alpha.5 * Remove older React versions from Travis * Add comments to connectAdvanced. Many much comments! * Re-add test for a custom store as a prop * Fix null pointer exception when store is given as a prop We were trying to read contextValue.subscription, even if that value was null. Reworked logic to handle cases where the store came in as a prop. * Ensure wrapper props are passed correctly when forwarding refs * Add a test to verify subscription passthrough with store-as-prop * add non-batched tests (reduxjs#1208) * Force SSR tests to mimic a Node environment * Restructure connect tests to group by category for easier reading Yeah, this kills the blame history. Sorry. But it's a lot easier to figure out what the tests are used for now. * Clean up dead code in Provider tests * Add tests to verify errors are thrown for bad mapState functions * Fix edge cases around saved wrapper props and error handling Changed to useLayoutEffect to ensure that the lastWrapperProps ref is written to synchronously when a render is committed. However, because React warns about this in SSR, added a check to fall back to plain useEffect under Node so we don't print warnings. Also added logic to ensure that if an error is thrown during a mapState function, but the component is unmounted before it can render, that the error will still be thrown. This shouldn't happen given our top-down subscriptions, but this will help surface the problem in our tests if we ever break the top-down behavior. * Formatting * Add a test to verify no errors are printed in SSR usage * Ignore .idea/ * 7.0.0-beta.0 * Updated outdated SSR-test (dispatch in ancestors) (reduxjs#1213) * Added test for injecting dynamic reducers on client and server (reduxjs#1211) * Remove WebStorm gitignore This goes in a global gitignore file, not a project. * [FIX]: reduxjs#1219 Save references before update (reduxjs#1220) * Re-ignore .idea/ * 7.0.0-beta.1 * Update the codecov config to be more forgiving. * add test to verify that mapStateToProps is always called with latest store state (reduxjs#1215)
I will create the corresponding test ASAP. But I'm quite confident that this fixes #1219
Update
This is what is happening:
Most of the times the update triggered by invoking the
dispatch
offorceComponentUpdateDispatch
happens "asynchronously" (in the next event loop). However, in other instances it happens synchronously.When it happens asynchronously, everything works as expected because the references are being updated before the next render. However, in those rare cases where the update happens immediately, the references have not been updated before the next render, which causes the following block:
To return the same
actualChildProps
that were evaluated in the previous render. Therefore, theuseMemo
hook ofrenderedChild
does not evaluate its compute function... That's why in those rare instances there is no update until the next dispatch.Mutating the references before the dispatch fixes the issue.
The problem is that I can't find a way to create a test that reproduces this behavior 😞 Maybe it's because I'm tired (I'm in CEST and it's almost 5am). So, I will go get some rest and see if tomorrow I can find a way to add a test for this... But that would be testing an implementation detail, so I'm not even sure that a proper test for this bug exists. Maybe tomorrow I will see it differently...
The only thing that I'm sure of is that while this change can not break anything, it can fix #1219. So, IMHO it's save to merge it.