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

Add getSnapshotBeforeUpdate lifecycle hook #1112

Merged
merged 10 commits into from May 27, 2018
Merged

Add getSnapshotBeforeUpdate lifecycle hook #1112

merged 10 commits into from May 27, 2018

Conversation

marvinhagemeister
Copy link
Member

@marvinhagemeister marvinhagemeister commented May 20, 2018

  • Add getSnapshotBeforeUpdate tests
  • Port unit tests from react
  • Add unit tests

@coveralls
Copy link

coveralls commented May 20, 2018

Coverage Status

Coverage remained the same at 100.0% when pulling 02abb3f on marvinhagemeister:snapshot_lifecycle into 1b898e2 on developit:master.

@@ -218,6 +218,181 @@ describe('Lifecycle methods', () => {
// [should not override state with stale values if prevState is spread within getDerivedStateFromProps](https://github.com/facebook/react/blob/25dda90c1ecb0c662ab06e2c80c1ee31e0ae9d36/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js#L1035)
});

describe.only("#getSnapshotBeforeUpdate", () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

TODO: remove only

rendered, inst, cbase;

// FIXME: Do we have an issue with previous state??
const prev = extend({}, previousState);
Copy link
Member Author

Choose a reason for hiding this comment

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

Dirty workaround: I have the suspicion that previousState is mutated somewhere. Caught this with the tests for getSnapshotBeforeUpdate by react.

TODO: Check what's happening here. Seems fishy and even more worrying is that we don't seem to have a test for that yet :S

@marvinhagemeister marvinhagemeister changed the title WIP: Add getSnapshotBeforeUpdate lifecycle hook Add getSnapshotBeforeUpdate lifecycle hook May 22, 2018
@@ -68,13 +68,14 @@ export function renderComponent(component, opts, mountAll, isChild) {
state = component.state,
context = component.context,
previousProps = component.prevProps || props,
previousState = component.prevState || state,
previousState = extend({}, component.prevState || state),
Copy link
Member Author

Choose a reason for hiding this comment

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

Although this is needed for getSnapshotBeforeUpdate this also fixes a bug with componentDidUpdate. During rendering, the state variable is mutated. This has the consequence, that previousState would never hold the previous state, but the most current one.

Copy link
Member

Choose a reason for hiding this comment

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

where were we mutating previousState?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, should have debugged this further. The mutation happens when getDerivedStateFromProps is used. Moved the extend call there, so that we limit the cloning and only do it when necessary.

@marvinhagemeister
Copy link
Member Author

This PR is now ready to review 🎉

previousContext = component.prevContext || context,
isUpdate = component.base,
nextBase = component.nextBase,
initialBase = isUpdate || nextBase,
initialChildComponent = component._component,
skip = false,
snapshot = previousContext,
Copy link
Member

Choose a reason for hiding this comment

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

Q: were we deviating from React here by passing context as a 3rd argument to componentDidUpdate? If so, maybe we correct that here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, we were/are deviating from React there. React never had a third context parameter. Same for shouldComponentUpdate. I'd prefer to make these changes in a separate PR, because they are independent from implementing getSnapshotBeforeUpdate.

Copy link
Member

Choose a reason for hiding this comment

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

Agreed regarding the separate PR, but we should make sure they go out in the same release - we'll save a few bytes by dropping that reassignment.

Copy link
Member Author

Choose a reason for hiding this comment

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

mh I have a branch where I removed the non-standard context arguments, but I get the exact same gzip size as in master :S

Copy link
Member

@andrewiggins andrewiggins left a comment

Choose a reason for hiding this comment

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

LGTM

@@ -218,6 +218,181 @@ describe('Lifecycle methods', () => {
// [should not override state with stale values if prevState is spread within getDerivedStateFromProps](https://github.com/facebook/react/blob/25dda90c1ecb0c662ab06e2c80c1ee31e0ae9d36/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js#L1035)
});

describe("#getSnapshotBeforeUpdate", () => {
it('should call nested new lifecycle methods in the right order', () => {
Copy link
Member

Choose a reason for hiding this comment

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

Love this test! This might be out of scope for this PR, but could we move this test to be directly under the "Lifecycle methods" describe block or put it in its own describe block? This test specificies the behavior of more than just getSnapshotBeforeUpdate. It specifies the order of (potentially all) lifecycle methods. I think it would be valuable to expand this test to include the order of the lifecycle methods for all 4 main render "events": mounting, props update, state update, and unmounting.

I started a test similar to this when implementing getDerivedStateFromProps but pulled it out as it was getting messy. This implementation is much more succinct. I'd be happy to expand this test in another PR if you think its useful.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a great suggestion! Moved the test to the top describe-block 🎉

I'm a bit hesitant to add componentWillUpdate, componentWillMount and componentWillReceiveProps to the test, because we'll follow react and mark them as deprecated with our next release.

Copy link
Member

Choose a reason for hiding this comment

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

Yea, that makes total sense. No need to add them

rendered, inst, cbase;

if (component.constructor.getDerivedStateFromProps) {
state = component.state = extend(state, component.constructor.getDerivedStateFromProps(props, state));
previousState = extend({}, previousState);
Copy link
Member

Choose a reason for hiding this comment

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

Good catch - thanks!

]);
});

it('should pass the return value from getSnapshotBeforeUpdate to componentDidUpdate', () => {
Copy link
Member

Choose a reason for hiding this comment

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

I appreciate the mirroring of the React UT suite 😄

Copy link
Member

@reznord reznord left a comment

Choose a reason for hiding this comment

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

LGTM

@marvinhagemeister marvinhagemeister merged commit 78ce6cd into preactjs:master May 27, 2018
@marvinhagemeister marvinhagemeister deleted the snapshot_lifecycle branch May 27, 2018 19:29
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

5 participants