Added memoization test for interrupted low-priority renders
Test added to verify that a high-priority update can reuse the children from an aborted low-priority update if shouldComponentUpdate returns false.
Brian Vaughn committed
Jan 13, 2017 at 13:46 UTC
2fb07b9502488424da1dee821bedd668f3cb9ba5
2 files changed
+84
scripts/fiber/tests-passing.txt
+1
@@ -1174,6 +1174,7 @@ src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
1174
* maintains the correct context when providers bail out due to low priority
1175
* maintains the correct context when unwinding due to an error in render
1176
* should not recreate masked context unless inputs have changed
1177
+* should reuse memoized work if pointers are updated before calling lifecycles
1178
1179
src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
1180
* catches render error in a boundary during full deferred mounting
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
+83
@@ -2184,4 +2184,87 @@ describe('ReactIncremental', () => {
2184
'componentDidUpdate',
2185
]);
2186
});
2187
+
2188
+ it('should reuse memoized work if pointers are updated before calling lifecycles', () => {
2189
+ let cduNextProps = [];
2190
+ let cduPrevProps = [];
2191
+ let scuNextProps = [];
2192
+ let scuPrevProps = [];
2193
+ let renderCounter = 0;
2194
+
2195
+ function SecondChild(props) {
2196
+ return <span>{props.children}</span>;
2197
+ }
2198
+
2199
+ class FirstChild extends React.Component {
2200
+ componentDidUpdate(prevProps, prevState) {
2201
+ cduNextProps.push(this.props);
2202
+ cduPrevProps.push(prevProps);
2203
+ }
2204
+ shouldComponentUpdate(nextProps, nextState) {
2205
+ scuNextProps.push(nextProps);
2206
+ scuPrevProps.push(this.props);
2207
+ return this.props.children !== nextProps.children;
2208
+ }
2209
+ render() {
2210
+ renderCounter++;
2211
+ return <span>{this.props.children}</span>;
2212
+ }
2213
+ }
2214
+
2215
+ class Middle extends React.Component {
2216
+ render() {
2217
+ return (
2218
+ <div>
2219
+ <FirstChild>{this.props.children}</FirstChild>
2220
+ <SecondChild>{this.props.children}</SecondChild>
2221
+ </div>
2222
+ );
2223
+ }
2224
+ }
2225
+
2226
+ function Root(props) {
2227
+ return (
2228
+ <div hidden={true}>
2229
+ <Middle {...props} />
2230
+ </div>
2231
+ );
2232
+ }
2233
+
2234
+ // Initial render of the entire tree.
2235
+ // Renders: Root, Middle, FirstChild, SecondChild
2236
+ ReactNoop.render(<Root>A</Root>);
2237
+ ReactNoop.flush();
2238
+
2239
+ expect(renderCounter).toBe(1);
2240
+
2241
+ // Schedule low priority work to update children.
2242
+ // Give it enough time to partially render.
2243
+ // Renders: Root, Middle, FirstChild
2244
+ ReactNoop.render(<Root>B</Root>);
2245
+ ReactNoop.flushDeferredPri(20 + 30 + 5);
2246
+
2247
+ // At this point our FirstChild component has rendered a second time,
2248
+ // But since the render is not completed cDU should not be called yet.
2249
+ expect(renderCounter).toBe(2);
2250
+ expect(scuPrevProps).toEqual([{ children: 'A' }]);
2251
+ expect(scuNextProps).toEqual([{ children: 'B' }]);
2252
+ expect(cduPrevProps).toEqual([]);
2253
+ expect(cduNextProps).toEqual([]);
2254
+
2255
+ // Next interrupt the partial render with higher priority work.
2256
+ // The in-progress child content will bailout.
2257
+ // Renders: Root, Middle, FirstChild, SecondChild
2258
+ ReactNoop.render(<Root>B</Root>);
2259
+ ReactNoop.flush();
2260
+
2261
+ // At this point the higher priority render has completed.
2262
+ // Since FirstChild props didn't change, sCU returned false.
2263
+ // The previous memoized copy should be used.
2264
+ expect(renderCounter).toBe(2);
2265
+ expect(scuPrevProps).toEqual([{ children: 'A' }, { children: 'B' }]);
2266
+ expect(scuNextProps).toEqual([{ children: 'B' }, { children: 'B' }]);
2267
+ expect(cduPrevProps).toEqual([{ children: 'A' }]);
2268
+ expect(cduNextProps).toEqual([{ children: 'B' }]);
2269
+ });
2270
});