diff --git a/src/renderers/shared/fiber/ReactFiberCompleteWork.js b/src/renderers/shared/fiber/ReactFiberCompleteWork.js index 4250f97bb0856..590332d5ce196 100644 --- a/src/renderers/shared/fiber/ReactFiberCompleteWork.js +++ b/src/renderers/shared/fiber/ReactFiberCompleteWork.js @@ -129,7 +129,14 @@ module.exports = function(config : HostConfig) { // Transfer update queue to callbackList field so callbacks can be // called during commit phase. workInProgress.callbackList = workInProgress.updateQueue; - markUpdate(workInProgress); + if (current) { + if (current.memoizedProps !== workInProgress.memoizedProps || + current.memoizedState !== workInProgress.memoizedState) { + markUpdate(workInProgress); + } + } else { + markUpdate(workInProgress); + } return null; case HostContainer: transferOutput(workInProgress.child, workInProgress); diff --git a/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js b/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js index 467965a8e937b..3a78f15fad57d 100644 --- a/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js +++ b/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js @@ -1234,4 +1234,103 @@ describe('ReactIncremental', () => { }); + it('skips will/DidUpdate when bailing unless an update was already in progress', () => { + var ops = []; + + class LifeCycle extends React.Component { + componentWillMount() { + ops.push('componentWillMount'); + } + componentDidMount() { + ops.push('componentDidMount'); + } + componentWillReceiveProps(nextProps) { + ops.push('componentWillReceiveProps'); + } + shouldComponentUpdate(nextProps) { + ops.push('shouldComponentUpdate'); + // Bail + return this.props.x !== nextProps.x; + } + componentWillUpdate(nextProps) { + ops.push('componentWillUpdate'); + } + componentDidUpdate(prevProps) { + ops.push('componentDidUpdate'); + } + render() { + ops.push('render'); + return ; + } + } + + function Sibling() { + ops.push('render sibling'); + return ; + } + + function App(props) { + return [ + , + , + ]; + } + + ReactNoop.render(); + ReactNoop.flush(); + + expect(ops).toEqual([ + 'componentWillMount', + 'render', + 'render sibling', + 'componentDidMount', + ]); + + ops = []; + + // Update to same props + ReactNoop.render(); + ReactNoop.flush(); + + expect(ops).toEqual([ + 'componentWillReceiveProps', + 'shouldComponentUpdate', + // no componenWillUpdate + // no render + 'render sibling', + // no componentDidUpdate + ]); + + ops = []; + + // Begin updating to new props... + ReactNoop.render(); + ReactNoop.flushDeferredPri(30); + + expect(ops).toEqual([ + 'componentWillReceiveProps', + 'shouldComponentUpdate', + 'componentWillUpdate', + 'render', + 'render sibling', + // no componentDidUpdate yet + ]); + + ops = []; + + // ...but we'll interrupt it to rerender the same props. + ReactNoop.render(); + ReactNoop.flush(); + + // We can bail out this time, but we must call componentDidUpdate. + expect(ops).toEqual([ + 'componentWillReceiveProps', + 'shouldComponentUpdate', + // no componentWillUpdate + // no render + 'render sibling', + 'componentDidUpdate', + ]); + }); + });