diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index f2a8ad1f2fe91..3c20dceacf3fb 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -169,12 +169,20 @@ export default function( function updateForwardRef(current, workInProgress) { const render = workInProgress.type.render; - const nextChildren = render( - workInProgress.pendingProps, - workInProgress.ref, - ); + const nextProps = workInProgress.pendingProps; + const ref = workInProgress.ref; + if (hasLegacyContextChanged()) { + // Normally we can bail out on props equality but if context has changed + // we don't do the bailout and we have to reuse existing props instead. + } else if (workInProgress.memoizedProps === nextProps) { + const currentRef = current !== null ? current.ref : null; + if (ref === currentRef) { + return bailoutOnAlreadyFinishedWork(current, workInProgress); + } + } + const nextChildren = render(nextProps, ref); reconcileChildren(current, workInProgress, nextChildren); - memoizeProps(workInProgress, nextChildren); + memoizeProps(workInProgress, nextProps); return workInProgress.child; } diff --git a/packages/react/src/__tests__/forwardRef-test.internal.js b/packages/react/src/__tests__/forwardRef-test.internal.js index 5780dbd054c6d..a578ae73680fe 100644 --- a/packages/react/src/__tests__/forwardRef-test.internal.js +++ b/packages/react/src/__tests__/forwardRef-test.internal.js @@ -232,6 +232,39 @@ describe('forwardRef', () => { expect(ref.current).toBe(null); }); + it('should not re-run the render callback on a deep setState', () => { + let inst; + + class Inner extends React.Component { + render() { + ReactNoop.yield('Inner'); + inst = this; + return
; + } + } + + function Middle(props) { + ReactNoop.yield('Middle'); + return ; + } + + const Forward = React.forwardRef((props, ref) => { + ReactNoop.yield('Forward'); + return ; + }); + + function App() { + ReactNoop.yield('App'); + return ; + } + + ReactNoop.render(); + expect(ReactNoop.flush()).toEqual(['App', 'Forward', 'Middle', 'Inner']); + + inst.setState({}); + expect(ReactNoop.flush()).toEqual(['Inner']); + }); + it('should warn if not provided a callback during creation', () => { expect(() => React.forwardRef(undefined)).toWarnDev( 'forwardRef requires a render function but was given undefined.',