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

Remove usages of async-unsafe lifecycle methods #919

Merged
merged 9 commits into from Jun 21, 2018
Merged

Remove usages of async-unsafe lifecycle methods #919

merged 9 commits into from Jun 21, 2018

Conversation

Hypnosphi
Copy link
Contributor

Fixes #897

@@ -2121,7 +2120,7 @@ describe('React', () => {

@connect(null)
class Parent extends React.Component {
componentWillMount() {
UNSAFE_componentWillMount() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

When I tried to change it to componentDidMount it broke the test. Maybe it should be changed to constructor, I'm not sure

Copy link
Member

Choose a reason for hiding this comment

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

What error did you get with cDM? The cWM function is not consequential here anyways; this is testing for unmount cleanups. So, I'd be fine with removing it and putting a dispatch outside of the ReactDOM.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.

data was null

}

shouldComponentUpdate() {
shouldComponentUpdate(nextProps) {
Copy link
Member

Choose a reason for hiding this comment

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

This feels like a hack with bad side effects. Can we use getSnapshotBeforeUpdate instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will try that. Is it called before sCU?

Copy link
Contributor Author

@Hypnosphi Hypnosphi Apr 3, 2018

Choose a reason for hiding this comment

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

Looks like it's not: https://reactjs.org/docs/react-component.html#updating

I can try to move selector from field on instance to field on state, then it will be available in static getDerivedStateFromProps

@@ -248,6 +247,10 @@ export default function connectAdvanced(

render() {
const selector = this.selector

// Handle forceUpdate
if (!selector.shouldComponentUpdate) selector.run(this.props)
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be handled by sCU above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sCU doesn't run when forceUpdating

@gaearon
Copy link
Contributor

gaearon commented Apr 3, 2018

Btw let us know when you'll need a final review for whether it's async-safe.
I'm not actively watching right now but we could take a look when PR is ready.

@Hypnosphi
Copy link
Contributor Author

@gaearon I think it's ready for review

@@ -2009,7 +2008,7 @@ describe('React', () => {

@connect(null, mapDispatchFactory, mergeParentDispatch)
class Passthrough extends Component {
componentWillUpdate() {
componentDIdUpdate() {
Copy link

@pke pke Apr 5, 2018

Choose a reason for hiding this comment

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

There is a typo here? componentDIdUpdate

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will fix & add test

Copy link
Member

Choose a reason for hiding this comment

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

Just commited a fix for you.

package.json Outdated
@@ -53,7 +53,8 @@
"lodash": "^4.17.5",
"lodash-es": "^4.17.5",
"loose-envify": "^1.1.0",
"prop-types": "^15.6.1"
"prop-types": "^15.6.1",
"react-lifecycles-compat": "^1.1.4"
Copy link

Choose a reason for hiding this comment

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

Why do you need the polyfill when you changed all the deprecated functions?

Copy link
Member

Choose a reason for hiding this comment

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

He's using getDerivedStateFromProps.

Copy link

Choose a reason for hiding this comment

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

I am using that too without a polyfill. No more warnings (in my own code).

Copy link
Member

Choose a reason for hiding this comment

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

It's for use with older versions of React. It's not to deal with deprecation warnings, it's to polyfill those lifecycle methods for React <= 16.2.

Copy link

Choose a reason for hiding this comment

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

Ah right, didn't think it that way around.
That brings me to a question for my own lib. What if I want to support the new and old Context API I guess I could just check if React.createContext is a function and then use it if avail?

Copy link
Contributor

Choose a reason for hiding this comment

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

@pke : there's a distinct difference in how you declare and use the old and new context APIs. See my PR in #898 for an example.

We may need to do that check for React.createContext so we can use a polyfill like create-react-context, but the old and new context APIs aren't interchangeable.

Copy link
Contributor

Choose a reason for hiding this comment

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

New context API is probably going to require breaking changes to React Redux's public API for supporting multiple stores, making it a major version bump.

@@ -129,7 +138,9 @@ export default function connectAdvanced(
`or explicitly pass "${storeKey}" as a prop to "${displayName}".`
)

this.initSelector()
this.state = {
selector: this.createSelector()
Copy link
Contributor

Choose a reason for hiding this comment

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

I haven't looked at what StrictMode warns about yet. Does it complain about instance variables? Is that why we're putting the selector into state?

Copy link
Contributor Author

@Hypnosphi Hypnosphi Apr 5, 2018

Choose a reason for hiding this comment

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

No it doesn't.

This is because getDerivedStateFromProps is static, so one can't access instances from it

Copy link
Contributor

Choose a reason for hiding this comment

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

Ahhh, I see it now. Got it.

@@ -86,6 +91,11 @@ export default function connectAdvanced(
[subscriptionKey]: subscriptionShape,
}

function getDerivedStateFromProps(nextProps, prevState) {
prevState.selector.run(nextProps)
Copy link
Contributor

@gaearon gaearon Apr 6, 2018

Choose a reason for hiding this comment

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

This looks mutative. What does it actually do?

I'm worried that this won't be safe and then avoiding the warning just gives a false sense of security.

The method signature is intentionally "get" to indicate it should be pure.

Copy link
Contributor Author

@Hypnosphi Hypnosphi Apr 6, 2018

Choose a reason for hiding this comment

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

It's not pure but it's idempotent: when you run in twice, it has the same effect as if it was just once.

I can try to make it pure by moving all the fields from selector to state though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's try that!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did that. It required some minor changes in tests: a7ba298#diff-b5fd7a42ea1b8efa382416b4a323003c

It's quite clear why setState calls count changed, but as for mapStateToPropsCalls, I'm not so confident

@timdorr
Copy link
Member

timdorr commented Apr 6, 2018

Well, now that the changes are becoming more significant, I wonder if we should pick #856 back up.

@markerikson
Copy link
Contributor

I think my changes in #898 supersede #856 .

We might want to consider how these would fit together.

@peacechen
Copy link

I'm testing with this fork and components subscribed to redux state using mapStateToProps don't trigger componentDidUpdate on state changes. Is there something else I need to set up?

@Hypnosphi
Copy link
Contributor Author

@peacechen it works OK on my project. Can you share some parts of your setup (maybe as a reproduction repo), so that I could debug it?

@peacechen
Copy link

It's a pretty large app. I'll try to create a small sample project to recreate the behavior.

Copy link
Member

@timdorr timdorr left a comment

Choose a reason for hiding this comment

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

I'll pull this into some apps to see what breaks. But I'm not seeing any major concerns with the changes proposed.

I would be interested in combining with #856 to move to a full React 16 dep. We still need to do 16.0-16.2 support, so the polyfill is necessary. But I don't consider that a huge issue for the moment.

@markerikson
Copy link
Contributor

markerikson commented Apr 20, 2018

Yeah, I suspect there's a lot of overlap between the two. I assume this would probably merit a v6.0 major version? So, notionally v6 would be "compat with React 16.3+", and v7 would be "super-duper React async rendering awesomeness" .

@timdorr
Copy link
Member

timdorr commented Apr 21, 2018

6.0 would be 16+. This supports <16.2, and Jim's PR does 16+. So, 16.0-16.2 would be covered. Definitely a major, though.

@timdorr
Copy link
Member

timdorr commented May 14, 2018

Anyone else want to second this? I can push a 5.1.0-beta.1 to give it a whirl elsewhere.

@ematipico
Copy link

I could try to integrate it into a small project of mine and leave some feedback, once the beta is out on npm.

@timdorr
Copy link
Member

timdorr commented Jun 21, 2018

OK, I'm going to merge this in and push a 5.1.0-test.1 release. Please give this a whirl! We can look into some of the BC-breaking stuff next.

@timdorr timdorr merged commit d16f759 into reduxjs:master Jun 21, 2018
@timdorr
Copy link
Member

timdorr commented Jun 21, 2018

Found a big bug with the gDSFP change in 16.4: https://reactjs.org/blog/2018/05/23/react-v-16-4.html#bugfix-for-getderivedstatefromprops

Because it's getting called for any render, it's getting double-called for most updates. That leads to it setting the sCU flag to true and then false on the 2nd call.

I've got to do a deeper dive, but I don't have time at the moment.

@timdorr
Copy link
Member

timdorr commented Jun 21, 2018

Well, I think (hope) it's just an issue with our test suite. So, react-redux@next is now on npm to try out.

josepot pushed a commit to josepot/react-redux-lean that referenced this pull request Sep 21, 2018
* Reformat to make VS Code less annoying

* Failing test in <StrictMode>

* Remove usages of async-unsafe lifecycle methods

* Remove usage of UNSAFE_componentWillMount in test

* Move `selector` to state in order to be able to use gDSFP

* codeDIdCommit

* Stop using mutation

* Upgrade react-lifecycles-compat
webguru7 pushed a commit to webguru7/react-redux that referenced this pull request Dec 8, 2022
* Reformat to make VS Code less annoying

* Failing test in <StrictMode>

* Remove usages of async-unsafe lifecycle methods

* Remove usage of UNSAFE_componentWillMount in test

* Move `selector` to state in order to be able to use gDSFP

* codeDIdCommit

* Stop using mutation

* Upgrade react-lifecycles-compat
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.

None yet

8 participants