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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix componentWillReceiveProps when calling setState/forceUpdate #2186
Conversation
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.
I think this is safe to do, do note that oldProps and newProps will never be referentially equal due to https://github.com/preactjs/preact/blob/master/src/create-element.js#L12
@JoviDeCroock This is not always true. For the component where |
Thanks @JoviDeCroock 馃檪I was also confused with that, but with the debugger I saw that the parent component (setting state or forceUpdate) kept the same equality for I've been playing around in this codesandbox here: https://codesandbox.io/s/preact-x-cwrp-b1inz Edit: Exactly, thanks @marvinhagemeister 馃槂 |
@@ -183,6 +183,59 @@ describe('Lifecycle methods', () => { | |||
expect(Inner.prototype.componentWillReceiveProps).to.have.been.called; | |||
}); | |||
|
|||
it('should be called when rerender with new props from parent even with setState/forceUpdate in child', () => { |
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.
This test is so good 馃槏
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.
This PR is awesome! The more I look at it, the more I'm convinced that the solution here is pretty genius!
The issue arises because both the Inner
and Outer
components are enqueued as part of the same commit. With each queued component we mark the update type with the _force
flag that is stored on the component class. Trouble arises because we expected the queued component to always be the root of the subtree it updates, never as one of the children of an already queued component. Thus our logic broke, because we checked each component for the _force
flag during diff, instead of just the one at the root of the update.
This solution here is a stroke of genius, because it leverages the fact that, the props
of a root component of any sub-tree will have referentially equal props. Every other vnode
below that will have a newly created object for props
like Jovi mentioned. Therefore checking for props equality is the smallest and easiest way to check for a root and assert if cWRP
should be called 馃帀
Love it, thank you so much for the PR 馃憤 馃挴
FYI: We'll likely publish a new release in a couple of hours 馃憤 |
Fixes #2185
componentWillReceiveProps
doesn't get called when callingsetState
orforceUpdate
and receiving props.componentWillReceiveProps
never gets called asthis.setState/forceUpdate
will setcomponent._force = false/true
, so_force
will not benull
orundefined
as expected indiff/index.js
:Instead of using the
_force
flag, I've decided to check the equality ofoldProps
andnewProps
to check whether props have changed. I've added a test for this case too. It would be great to know if I'm missing an edge case with this fix?This fix also drops the size by
-6B
馃帀