Skip to content

Commit

Permalink
Added memoization test for interrupted low-priority renders
Browse files Browse the repository at this point in the history
Test added to verify that a high-priority update can reuse the children from an aborted low-priority update if shouldComponentUpdate returns false.
  • Loading branch information
Brian Vaughn committed Jan 13, 2017
1 parent 20c4aac commit 2fb07b9
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 0 deletions.
1 change: 1 addition & 0 deletions scripts/fiber/tests-passing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,7 @@ src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
* maintains the correct context when providers bail out due to low priority
* maintains the correct context when unwinding due to an error in render
* should not recreate masked context unless inputs have changed
* should reuse memoized work if pointers are updated before calling lifecycles

src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
* catches render error in a boundary during full deferred mounting
Expand Down
83 changes: 83 additions & 0 deletions src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2184,4 +2184,87 @@ describe('ReactIncremental', () => {
'componentDidUpdate',
]);
});

it('should reuse memoized work if pointers are updated before calling lifecycles', () => {
let cduNextProps = [];
let cduPrevProps = [];
let scuNextProps = [];
let scuPrevProps = [];
let renderCounter = 0;

function SecondChild(props) {
return <span>{props.children}</span>;
}

class FirstChild extends React.Component {
componentDidUpdate(prevProps, prevState) {
cduNextProps.push(this.props);
cduPrevProps.push(prevProps);
}
shouldComponentUpdate(nextProps, nextState) {
scuNextProps.push(nextProps);
scuPrevProps.push(this.props);
return this.props.children !== nextProps.children;
}
render() {
renderCounter++;
return <span>{this.props.children}</span>;
}
}

class Middle extends React.Component {
render() {
return (
<div>
<FirstChild>{this.props.children}</FirstChild>
<SecondChild>{this.props.children}</SecondChild>
</div>
);
}
}

function Root(props) {
return (
<div hidden={true}>
<Middle {...props} />
</div>
);
}

// Initial render of the entire tree.
// Renders: Root, Middle, FirstChild, SecondChild
ReactNoop.render(<Root>A</Root>);
ReactNoop.flush();

expect(renderCounter).toBe(1);

// Schedule low priority work to update children.
// Give it enough time to partially render.
// Renders: Root, Middle, FirstChild
ReactNoop.render(<Root>B</Root>);
ReactNoop.flushDeferredPri(20 + 30 + 5);

// At this point our FirstChild component has rendered a second time,
// But since the render is not completed cDU should not be called yet.
expect(renderCounter).toBe(2);
expect(scuPrevProps).toEqual([{ children: 'A' }]);
expect(scuNextProps).toEqual([{ children: 'B' }]);
expect(cduPrevProps).toEqual([]);
expect(cduNextProps).toEqual([]);

// Next interrupt the partial render with higher priority work.
// The in-progress child content will bailout.
// Renders: Root, Middle, FirstChild, SecondChild
ReactNoop.render(<Root>B</Root>);
ReactNoop.flush();

// At this point the higher priority render has completed.
// Since FirstChild props didn't change, sCU returned false.
// The previous memoized copy should be used.
expect(renderCounter).toBe(2);
expect(scuPrevProps).toEqual([{ children: 'A' }, { children: 'B' }]);
expect(scuNextProps).toEqual([{ children: 'B' }, { children: 'B' }]);
expect(cduPrevProps).toEqual([{ children: 'A' }]);
expect(cduNextProps).toEqual([{ children: 'B' }]);
});
});

0 comments on commit 2fb07b9

Please sign in to comment.