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

Convert Provider into function component with hooks #1377

Merged
merged 2 commits into from
Aug 20, 2019

Conversation

mpeyper
Copy link
Contributor

@mpeyper mpeyper commented Aug 11, 2019

Fixes issue where the store and children values were updated in different render passes which could result in mapState errors if the new children were not compatible with the old store.

The change to a function component meant Provider could take advantage of useEffect to "watch" the store prop and unsubscribe to the old store and subscribe to the new store.

Fixes #1370

Fixes issue where the store and children got updated in different render passes which could result in mapState errors if the new children were not compatible with the old store.
Fixes reduxjs#1370
subscription.onStateChange = this.notifySubscribers

this.state = {
subscription.onStateChange = subscription.notifyNestedSubs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I determined subscription.onStateChange = this.notifySubscribers to actually boiled down to. I'm wondering if subscription couldn't just handle this internally?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use the same Subscription class for the connect components, where this is part of our top-down update mechanic. So, it needs to stay configurable. This is just a quirk of re-use.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is a very old PR, but I'm wondering if you ever tested this in StrictMode? I'm noticing bugs where updates stop happening for us using react-redux 7.2.5, and I believe it's because we set onStateChange here in the useMemo callback, but then we clear it here in the useEffect teardown. With strict mode, the useMemo callback here will run twice, but the result of the second run isn't actually used. So, the result is that children are rendered with the original subscription which is resubscribed but has no onStateChange callback, so it does nothing. I think the fix is to move this line to the useEffect call. I can patch that for us locally, but I'm wondering if you ever encountered this after making this change.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I see 6acb58d now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MichaelSnowden yeah, we actually fixed that bug in https://github.com/reduxjs/react-redux/releases/tag/v7.2.8 :) latest 7.x release is 7.2.9. (But also, we would recommend upgrading to v8.x if possible if you're still on React 17 or earlier, or v9 if you're on React 18.)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @markerikson ! Do you know if it's possible for this to occur outside of StrictMode with React 18? I don't think we'll be able to upgrade unfortunately, but we can patch this fix. The main thing I'm wondering is if this could explain other times I've heard devs say that the Redux store just stopped updating their components (even outside of strict mode).

I don't understand React concurrent mode fully, but it seems like it might be possible there for something simillar to happen there where the subscription useMemo state is reused, but this effect runs twice (because the component remounted but kept its state somehow). Do you know if that's possible or if this is a bug we could have only seen in strict mode.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strict Mode would be the only real reason I can think of that this would be an issue off the top of my head.

@netlify
Copy link

netlify bot commented Aug 11, 2019

Deploy preview for react-redux-docs ready!

Built with commit 67e873d

https://deploy-preview-1377--react-redux-docs.netlify.com

@@ -312,5 +312,43 @@ describe('React', () => {
ReactDOM.unmountComponentAtNode(div)
expect(spy).toHaveBeenCalledTimes(1)
})

it('should handle store and children change in a the same render', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did verify that this did actually fail on the old code before making my change.

}

componentWillUnmount() {
if (this.unsubscribe) this.unsubscribe()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not see where this.unsubscribe was actually being set. I think this could have been safely removed, but the new implementation does not allow for this in any way, so if that is an issue I'll have to go back to the drawing board.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think this line is a leftover artifact from pre-7.0 that I missed removing.

@markerikson
Copy link
Contributor

Hey, thanks for putting this together! I'll have to come back and look at it a bit later, but noted.

@mpeyper
Copy link
Contributor Author

mpeyper commented Aug 19, 2019

hey @markerikson / @timdorr, have either of you had a chance to take a look at this yet?

@timdorr
Copy link
Member

timdorr commented Aug 19, 2019

Looks good to me. @markerikson?

@markerikson
Copy link
Contributor

Okay, board is green. LGTM!

@markerikson markerikson merged commit 0c5f764 into reduxjs:master Aug 20, 2019
@mpeyper
Copy link
Contributor Author

mpeyper commented Aug 22, 2019

Hey, just curious what the release timeframe is on this. Is there a specific milestone this will wait for or will a 7.1.1 version get pushed out soon?

@markerikson
Copy link
Contributor

Good question. @timdorr , you got time to drop a new release with this and the memory clear tweak?

I'm at React Rally this week. Busy with the conf the next two days. Hoping to do some maintainer-ish stuff on the weekend. If it's not out by then, I'll try to publish it.

@mpeyper
Copy link
Contributor Author

mpeyper commented Aug 22, 2019

No major rush from me, just curious.

@mpeyper mpeyper deleted the fix-1370 branch August 22, 2019 04:55
@timdorr
Copy link
Member

timdorr commented Aug 26, 2019

This is out as 7.1.1 now!

aaronlademann-wf added a commit to Workiva/react-dart that referenced this pull request Dec 11, 2019
+ 7.1.1 and above cause widespread test failures as a result of them changing to the use of a function component / hooks: reduxjs/react-redux#1377
keepsimple7 added a commit to keepsimple7/react-dart-expander that referenced this pull request Aug 15, 2022
+ 7.1.1 and above cause widespread test failures as a result of them changing to the use of a function component / hooks: reduxjs/react-redux#1377
albertodev7 pushed a commit to albertodev7/react-redux that referenced this pull request Dec 8, 2022
* Convert Provider into function component with hooks

Fixes issue where the store and children got updated in different render passes which could result in mapState errors if the new children were not compatible with the old store.
Fixes reduxjs#1370

* Remove onStateChange from subscription when unsubscribing

Co-Authored-By: Mark Erikson <[email protected]>
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

Successfully merging this pull request may close these issues.

Provider uses previous store for one additional render when store changes
4 participants