From d1bb4d851f4956cb64ad0ae1867153c23409f971 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 20 Oct 2020 14:35:57 -0400 Subject: [PATCH] Profiler: Include ref callbacks in onCommit duration (#20060) --- .../src/ReactFiberCommitWork.new.js | 58 +++++++++++++++++-- .../src/ReactFiberCommitWork.old.js | 58 +++++++++++++++++-- .../__tests__/ReactProfiler-test.internal.js | 55 ++++++++++++++++++ 3 files changed, 163 insertions(+), 8 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index 687e62de976c7..3664803f20438 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -194,14 +194,38 @@ function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber) { if (ref !== null) { if (typeof ref === 'function') { if (__DEV__) { - invokeGuardedCallback(null, ref, null, null); + if ( + enableProfilerTimer && + enableProfilerCommitHooks && + current.mode & ProfileMode + ) { + startLayoutEffectTimer(); + invokeGuardedCallback(null, ref, null, null); + recordLayoutEffectDuration(current); + } else { + invokeGuardedCallback(null, ref, null, null); + } + if (hasCaughtError()) { const refError = clearCaughtError(); captureCommitPhaseError(current, nearestMountedAncestor, refError); } } else { try { - ref(null); + if ( + enableProfilerTimer && + enableProfilerCommitHooks && + current.mode & ProfileMode + ) { + try { + startLayoutEffectTimer(); + ref(null); + } finally { + recordLayoutEffectDuration(current); + } + } else { + ref(null); + } } catch (refError) { captureCommitPhaseError(current, nearestMountedAncestor, refError); } @@ -965,7 +989,20 @@ function commitAttachRef(finishedWork: Fiber) { instanceToUse = instance; } if (typeof ref === 'function') { - ref(instanceToUse); + if ( + enableProfilerTimer && + enableProfilerCommitHooks && + finishedWork.mode & ProfileMode + ) { + try { + startLayoutEffectTimer(); + ref(instanceToUse); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { + ref(instanceToUse); + } } else { if (__DEV__) { if (!ref.hasOwnProperty('current')) { @@ -986,7 +1023,20 @@ function commitDetachRef(current: Fiber) { const currentRef = current.ref; if (currentRef !== null) { if (typeof currentRef === 'function') { - currentRef(null); + if ( + enableProfilerTimer && + enableProfilerCommitHooks && + current.mode & ProfileMode + ) { + try { + startLayoutEffectTimer(); + currentRef(null); + } finally { + recordLayoutEffectDuration(current); + } + } else { + currentRef(null); + } } else { currentRef.current = null; } diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index e094e5bd9b5a8..84ff06fe7f998 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -180,14 +180,38 @@ function safelyDetachRef(current: Fiber) { if (ref !== null) { if (typeof ref === 'function') { if (__DEV__) { - invokeGuardedCallback(null, ref, null, null); + if ( + enableProfilerTimer && + enableProfilerCommitHooks && + current.mode & ProfileMode + ) { + startLayoutEffectTimer(); + invokeGuardedCallback(null, ref, null, null); + recordLayoutEffectDuration(current); + } else { + invokeGuardedCallback(null, ref, null, null); + } + if (hasCaughtError()) { const refError = clearCaughtError(); captureCommitPhaseError(current, refError); } } else { try { - ref(null); + if ( + enableProfilerTimer && + enableProfilerCommitHooks && + current.mode & ProfileMode + ) { + try { + startLayoutEffectTimer(); + ref(null); + } finally { + recordLayoutEffectDuration(current); + } + } else { + ref(null); + } } catch (refError) { captureCommitPhaseError(current, refError); } @@ -832,7 +856,20 @@ function commitAttachRef(finishedWork: Fiber) { instanceToUse = instance; } if (typeof ref === 'function') { - ref(instanceToUse); + if ( + enableProfilerTimer && + enableProfilerCommitHooks && + finishedWork.mode & ProfileMode + ) { + try { + startLayoutEffectTimer(); + ref(instanceToUse); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { + ref(instanceToUse); + } } else { if (__DEV__) { if (!ref.hasOwnProperty('current')) { @@ -853,7 +890,20 @@ function commitDetachRef(current: Fiber) { const currentRef = current.ref; if (currentRef !== null) { if (typeof currentRef === 'function') { - currentRef(null); + if ( + enableProfilerTimer && + enableProfilerCommitHooks && + current.mode & ProfileMode + ) { + try { + startLayoutEffectTimer(); + currentRef(null); + } finally { + recordLayoutEffectDuration(current); + } + } else { + currentRef(null); + } } else { currentRef.current = null; } diff --git a/packages/react/src/__tests__/ReactProfiler-test.internal.js b/packages/react/src/__tests__/ReactProfiler-test.internal.js index 180eb81b964bf..73812de49a3b0 100644 --- a/packages/react/src/__tests__/ReactProfiler-test.internal.js +++ b/packages/react/src/__tests__/ReactProfiler-test.internal.js @@ -1491,6 +1491,61 @@ describe('Profiler', () => { expect(call[4]).toEqual(enableSchedulerTracing ? new Set() : undefined); // interaction events }); + it('should include time spent in ref callbacks', () => { + const callback = jest.fn(); + + const refSetter = ref => { + if (ref !== null) { + Scheduler.unstable_advanceTime(10); + } else { + Scheduler.unstable_advanceTime(100); + } + }; + + class ClassComponent extends React.Component { + render() { + return null; + } + } + + const Component = () => { + Scheduler.unstable_advanceTime(1000); + return ; + }; + + Scheduler.unstable_advanceTime(1); + + const renderer = ReactTestRenderer.create( + + + , + ); + + expect(callback).toHaveBeenCalledTimes(1); + + let call = callback.mock.calls[0]; + + expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4); + expect(call[0]).toBe('root'); + expect(call[1]).toBe('mount'); + expect(call[2]).toBe(10); // durations + expect(call[3]).toBe(1001); // commit start time (before mutations or effects) + + callback.mockClear(); + + renderer.update(); + + expect(callback).toHaveBeenCalledTimes(1); + + call = callback.mock.calls[0]; + + expect(call).toHaveLength(enableSchedulerTracing ? 5 : 4); + expect(call[0]).toBe('root'); + expect(call[1]).toBe('update'); + expect(call[2]).toBe(100); // durations + expect(call[3]).toBe(1011); // commit start time (before mutations or effects) + }); + it('should bubble time spent in layout effects to higher profilers', () => { const callback = jest.fn();