import * as React from 'react' import { createTimer } from './timer' import { addDurationVital } from './addDurationVital' /** * Track the performance of a React component. * * @experimental */ // eslint-disable-next-line export const UNSTABLE_ReactComponentTracker = ({ name: componentName, children, }: { name: string children?: React.ReactNode }) => { const isFirstRender = React.useRef(true) const renderTimer = createTimer() const effectTimer = createTimer() const layoutEffectTimer = createTimer() const onEffectEnd = () => { const renderDuration = renderTimer.getDuration() ?? 0 const effectDuration = effectTimer.getDuration() ?? 0 const layoutEffectDuration = layoutEffectTimer.getDuration() ?? 0 const totalRenderTime = renderDuration + effectDuration + layoutEffectDuration addDurationVital('reactComponentRender', { description: componentName, startTime: renderTimer.getStartTime()!, // note: renderTimer should have been started at this point, so getStartTime should not return undefined duration: totalRenderTime, context: { is_first_render: isFirstRender.current, render_phase_duration: renderDuration, effect_phase_duration: effectDuration, layout_effect_phase_duration: layoutEffectDuration, framework: 'react', }, }) isFirstRender.current = false } // In react, children are rendered sequentially in the order they are defined. that's why we can // measure perf timings of a component by starting recordings in the component above and stopping // them in the component below. return ( <> {children} { effectTimer.stopTimer() onEffectEnd() }} /> ) } function LifeCycle({ onRender, onLayoutEffect, onEffect, }: { onRender: () => void onLayoutEffect: () => void onEffect: () => void }) { onRender() React.useLayoutEffect(onLayoutEffect) React.useEffect(onEffect) return null }