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

Added memoization test for interrupted low-priority renders #8645

Merged
merged 1 commit into from
Jan 13, 2017

Conversation

bvaughn
Copy link
Contributor

@bvaughn bvaughn commented Dec 27, 2016

Added a test to verify that a high-priority update can reuse the children from an aborted low-priority update if shouldComponentUpdate returns false.

(Note that this PR is now different from the original. The original description is preserved below so that the subsequent discussion on this PR makes sense.)

Previous Description (no longer valid)

this.props and this.context are not reset when a render is interrupted mid-way and overridden by a subsequent, higher-priority update. The result is that a subsequent calls to shouldComponentUpdate will yield unexpected results1 when comparing this.props and this.context to the provided nexProps and nexContext.

This PR captures the behavior in a (failing) unit test. Posting for discussion purposes.

  • 1 It is unclear if this is intentional behavior. I don't think it causes any problems but it was not what I expected initially.

@bvaughn
Copy link
Contributor Author

bvaughn commented Dec 27, 2016

FYI @gaearon, this is the failing test we were discussing on PR #8631.

The same failure highlighted in this test applies to context as well as props but since context is already in a broken state in master the test focuses on props only.

Do you think it would be useful for me to expand the test to include a check for context as well (or to add a second test for that)? I am kind of on the fence about it.

@gaearon
Copy link
Collaborator

gaearon commented Dec 27, 2016

Hmm maybe I misunderstand but I thought this was intentional. You get "last attempted" as prev in sCU but "last rendered" as prev in cDU. Is this a problem in some scenario?

@bvaughn bvaughn force-pushed the add-failing-scu-current-props-test branch from 9ee0624 to 382fd04 Compare December 27, 2016 17:13
@bvaughn
Copy link
Contributor Author

bvaughn commented Dec 27, 2016

Hmm. It's possible it is intentional and I just misunderstood the intent. That would make sense too as a behavior. I suppose I just assumed symmetry of before and after values between those methods (cWRP/sCU/cWU/cDU).

You get "last attempted" as prev in sCU but "last rendered" as prev in cDU

I'm not sure people think of this.props as "last attempted" but I could be wrong. (I don't think most people will be thinking of render attempts or bail-outs to begin with.)

Either way, I guess you're right. I don't think it would cause problems unless users set some sort of expectations in cWRP/sCU/cWU that were checked in cDU which seems unlikely. Looking at the different ways in which context or props might change (or not change) between an interrupted render and the next potential render- I think the current behavior is okay so long as it is intentional.

Maybe @sebmarkbage can clarify or point out a case I'm not thinking about. We talked about this briefly last week in the context of a larger discussion about context but now I'm second-guessing my understanding of this part of that conversation. 😄

If this is indeed the expected behavior then perhaps I could modify my test slightly (so that it passes) and capture it as such.

@bvaughn bvaughn changed the title Add failing test for sCU current props Add failing test for unexpected prevProps passed to cDU Dec 27, 2016
@bvaughn bvaughn changed the title Add failing test for unexpected prevProps passed to cDU Add failing test for unexpected this.props value Dec 27, 2016
@acdlite
Copy link
Collaborator

acdlite commented Dec 29, 2016

Thought about this for a bit.

The mental model for shouldComponentUpdate is memoization. But I think it's a memoization of workInProgress.child, not current.child. If sCU returns false, that only means that we don't need to recompute our children. It does not mean that those children were already flushed.

To put this in more practical terms, if a high priority update interrupts a low priority update before it is flushed, we should have the ability to reuse the children computed during the low priority update. Which we can't do if we reset the pointers. I don't think we have a test for this, but we should.

Care to write one? :)

Copy link
Collaborator

@acdlite acdlite left a comment

Choose a reason for hiding this comment

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

Change test to check that a high-priority update can reuse the children from an aborted low-priority update using shouldComponentUpdate.

@acdlite
Copy link
Collaborator

acdlite commented Dec 29, 2016

I don't think it would cause problems unless users set some sort of expectations in cWRP/sCU/cWU that were checked in cDU which seems unlikely.

I agree that this is unlikely. A better name for shouldComponentUpdate might be areInputsEqual, since that makes no promises about the implementation details of memoization.

@acdlite
Copy link
Collaborator

acdlite commented Dec 30, 2016

^ The test I describe above happens to be related to a PR I was already working on, so I added it there. https://github.com/facebook/react/pull/8655/files#diff-6a068060b8f49a9d61ae16cac3be4a9b

@bvaughn
Copy link
Contributor Author

bvaughn commented Dec 31, 2016

I don't think we have a test for this, but we should.
Care to write one? :)

Wouldn't that essentially be the test I've added, if I only changed an expectation or two (and maybe added a render counter)? That's why I left the previous comment:

If this is indeed the expected behavior then perhaps I could modify my test slightly (so that it passes) and capture it as such.

I'll update the test in this PR.

@bvaughn
Copy link
Contributor Author

bvaughn commented Dec 31, 2016

Thanks for the input Dan and Andrew. I've updated this PR to capture what I think has been agreed on as the expected/correct behavior (a new passing test instead of a failing one).

@bvaughn bvaughn changed the title Add failing test for unexpected this.props value Added memoization test for interrupted low-priority renders Dec 31, 2016
// Renders: Root
ReactNoop.render(<Root>B</Root>);
ReactNoop.flushDeferredPri(20);

Copy link
Collaborator

Choose a reason for hiding this comment

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

Should you be asserting that what you expected happened? Otherwise you can merge the call below into this one.

expect(scuPrevProps.children).toEqual('A');
expect(scuNextProps.children).toEqual('B');
expect(cduPrevProps).toEqual(null);
expect(cduNextProps).toEqual(null);
Copy link
Collaborator

Choose a reason for hiding this comment

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

These could have been called with null if there was a bug. I tend to prefer pushing to an array since that covers that and tests execution order.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion.

@bvaughn bvaughn force-pushed the add-failing-scu-current-props-test branch from d814ef5 to 0e954f9 Compare December 31, 2016 04:40
@bvaughn bvaughn force-pushed the add-failing-scu-current-props-test branch from 0e954f9 to e95aaa7 Compare January 13, 2017 21:34
@@ -2184,4 +2184,87 @@ describe('ReactIncremental', () => {
'componentDidUpdate',
]);
});

it('should reuse memoized work after an interrupted low-priority render', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's rename the test so it's clear that this is specifically about updating the pointers before calling lifecycles.

Copy link
Collaborator

@acdlite acdlite left a comment

Choose a reason for hiding this comment

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

👍

Test added to verify that a high-priority update can reuse the children from an aborted low-priority update if shouldComponentUpdate returns false.
@bvaughn bvaughn force-pushed the add-failing-scu-current-props-test branch from e95aaa7 to 2fb07b9 Compare January 13, 2017 21:46
@bvaughn bvaughn merged commit c359df7 into facebook:master Jan 13, 2017
@bvaughn bvaughn deleted the add-failing-scu-current-props-test branch January 13, 2017 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants