diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 81f2081f2fe4a..fc47f3d148484 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -41,6 +41,8 @@ if (__DEV__) { function createReactNoop(reconciler: Function, useMutation: boolean) { let scheduledCallback = null; let instanceCounter = 0; + let hostDiffCounter = 0; + let hostUpdateCounter = 0; function appendChildToContainerOrInstance( parentInstance: Container | Instance, @@ -220,6 +222,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { if (newProps === null) { throw new Error('Should have new props'); } + hostDiffCounter++; return UPDATE_SIGNAL; }, @@ -303,6 +306,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { if (oldProps === null) { throw new Error('Should have old props'); } + hostUpdateCounter++; instance.prop = newProps.prop; }, @@ -311,6 +315,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { oldText: string, newText: string, ): void { + hostUpdateCounter++; textInstance.text = newText; }, @@ -556,6 +561,21 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { return actual !== null ? actual : []; }, + flushWithHostCounters(fn) { + hostDiffCounter = 0; + hostUpdateCounter = 0; + try { + ReactNoop.flush(); + return { + hostDiffCounter, + hostUpdateCounter, + }; + } finally { + hostDiffCounter = 0; + hostUpdateCounter = 0; + } + }, + expire(ms: number): Array { ReactNoop.advanceTime(ms); return ReactNoop.flushExpired(); diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdatesMinimalism-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdatesMinimalism-test.js new file mode 100644 index 0000000000000..7696403081ccb --- /dev/null +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdatesMinimalism-test.js @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails react-core + * @jest-environment node + */ + +'use strict'; + +let React; +let ReactNoop; + +describe('ReactIncrementalUpdatesMinimalism', () => { + beforeEach(() => { + jest.resetModules(); + React = require('react'); + ReactNoop = require('react-noop-renderer'); + }); + + it('should render a simple component', () => { + function Child() { + return
Hello World
; + } + + function Parent() { + return ; + } + + ReactNoop.render(); + expect(ReactNoop.flushWithHostCounters()).toEqual({ + hostDiffCounter: 0, + hostUpdateCounter: 0, + }); + + ReactNoop.render(); + expect(ReactNoop.flushWithHostCounters()).toEqual({ + hostDiffCounter: 1, + hostUpdateCounter: 1, + }); + }); + + it('should not diff referentially equal host elements', () => { + function Leaf(props) { + return ( + + hello + + {props.name} + + ); + } + + const constEl = ( +
+ +
+ ); + + function Child() { + return constEl; + } + + function Parent() { + return ; + } + + ReactNoop.render(); + expect(ReactNoop.flushWithHostCounters()).toEqual({ + hostDiffCounter: 0, + hostUpdateCounter: 0, + }); + + ReactNoop.render(); + expect(ReactNoop.flushWithHostCounters()).toEqual({ + hostDiffCounter: 0, + hostUpdateCounter: 0, + }); + }); + + it('should not diff parents of setState targets', () => { + let childInst; + + function Leaf(props) { + return ( + + hello + + {props.name} + + ); + } + + class Child extends React.Component { + state = {name: 'Batman'}; + render() { + childInst = this; + return ( +
+ +
+ ); + } + } + + function Parent() { + return ( +
+
+ + + +
+
+ ); + } + + ReactNoop.render(); + expect(ReactNoop.flushWithHostCounters()).toEqual({ + hostDiffCounter: 0, + hostUpdateCounter: 0, + }); + + childInst.setState({name: 'Robin'}); + expect(ReactNoop.flushWithHostCounters()).toEqual({ + // Child > div + // Child > Leaf > span + // Child > Leaf > span > b + hostDiffCounter: 3, + // Child > div + // Child > Leaf > span + // Child > Leaf > span > b + // Child > Leaf > span > #text + hostUpdateCounter: 4, + }); + + ReactNoop.render(); + expect(ReactNoop.flushWithHostCounters()).toEqual({ + // Parent > section + // Parent > section > div + // Parent > section > div > Leaf > span + // Parent > section > div > Leaf > span > b + // Parent > section > div > Child > div + // Parent > section > div > Child > div > Leaf > span + // Parent > section > div > Child > div > Leaf > span > b + // Parent > section > div > Leaf > span + // Parent > section > div > Leaf > span > b + hostDiffCounter: 9, + hostUpdateCounter: 9, + }); + }); +});