@samitouri / QOS-React-2 / commits / 2fb07b9502

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 });