Pull Request Overview
- Opened on October 12, 2026
- Status Open
- Commit count 1 with first commit October 12, 2026
Total Delta
Open Days
Test Delta
How long has this pull request spent in each phase of its lifecycle?
| Fraction of total time | Business days | Phase |
|---|---|---|
| N/A | 6.8 days | Issue creation to work beginning |
|
|
0.0 days | Authoring 1 commit before pull request opened for review |
|
|
0.0 days | Awaiting first review |
|
|
24.0 days | Revising work with 0 commits in response to 0 reviews that left 1 comment |
Total time for pull request still awaiting merge (longer than repo's target): 24.0 business days
[DevTools] Fix Profiler crash when recording why a memo() wrapper rendered
Summary
Fixes #37733.
With Record why each component rendered while profiling enabled, re-rendering a memo(forwardRef(...)) component with new props throws Unknown Fiber. Needs to be a function component to inspect hooks. from the commit hook. DevTools reports it as "React instrumentation encountered an error". memo(fn, compare) hits the same error.
This is a regression from #35123. getChangeDescription used to read fiber.memoizedState directly. That is null on a MemoComponent, so it reported hooks: null. Now it calls inspectHooks() on the previous and next fiber for every function-like tag, but inspectHooksOfFiber only accepts FunctionComponent, SimpleMemoComponent and ForwardRef. React creates a MemoComponent fiber for memo() of anything other than a plain function without a compare function. It is only a wrapper, and its child fiber owns the hooks. The existing test only covered a memo(Child, areEqual) that bails out, so this path never ran.
The fix only inspects hooks when both fibers have one of those tags, which is the same gate inspectElement uses (usesHooks). It also requires the renderer to inject currentDispatcherRef, which shipped with hooks in 16.8. React 16.5โ16.7 still support profiling, but inspectHooksOfFiber throws there for every function component: those fibers only have firstContextDependency, so it fails with "Unsupported React version". It also throws for forwardRef on 16.5, where the tag is 13. In all other cases the change description reports hooks: null, as it did before #35123, and the frontend already handles that. The inner forwardRef or function fiber still reports its own hook changes.
Not addressed here: since #35123, the commit path re-runs component render functions. A component that throws when re-rendered with the previous fiber's props also crashes the instrumentation, with ReactDebugToolsRenderError. One example is a component that reads an item that has since been removed from an external store. That is a separate issue.
How did you test this change?
- Added two tests to
profilerChangeDescriptions-test.js:-
memo(forwardRef()), in the issue's shape: a controlled input with an inlineonChange. It also checks that a state update inside theforwardRefis still reported (didHooksChange: true, hooks: [0]) while the memo wrapper bails out. -
memo()with a custom compare function.
-
Both fail on main with "Unknown Fiber. Needs to be a function component to inspect hooks." and pass with this change.
- yarn test --build --project=devtools: 597 pass. The only failure is inspectedElement-test "inspecting nested renderers should not throw (createRoot)", which fails the same way on main locally (react-art's canvas setTransform is null in jsdom). The source and www Jest configs exclude the DevTools packages, so yarn test and yarn test-www match no tests.
- Flow with the dom-browser config, which covers react-devtools-shared: no errors. yarn prettier and yarn linc: pass.
- I didn't run the 16.5 regression lane locally. The pre-16.8 gate is based on reading the 16.5โ16.8 reconciler source.
Website or app
https://stackblitz.com/edit/vitejs-vite-qfa8tyuo?file=src/App.tsx
Repro steps
React version
- React: 19.3.0
- React DOM: 19.3.0
- React Developer Tools Chrome extension: 8.0.0
- Extension revision:
1d34f91dfd
Reproduction
Important: open the StackBlitz preview in a separate browser tab. React DevTools cannot profile the embedded StackBlitz iframe.
Steps to reproduce
- Open the StackBlitz project above.
- Open its preview in a separate tab.
- Open React Developer Tools โ Profiler.
- In Profiler settings, enable Record why each component rendered while profiling.
- Start a profiling recording.
- Type one character into the input.
- Stop the recording.
Actual behavior
React DevTools reports:
React instrumentation encountered an error:
Error: Unknown Fiber. Needs to be a function component to inspect hooks.
at inspectHooksOfFiberImpl (installHook.js)
at inspectHooksOfFiber (installHook.js)
at inspectHooks (installHook.js)
at recordProfilingDurations (installHook.js)
at updateFiberRecursively (installHook.js)
The error is triggered by a controlled component implemented as memo(forwardRef(...)) when its parent re-renders and supplies a new inline onChange callback.
Typing changes the controlled value and re-renders the memoized forward-ref input with changed props. This triggers the DevTools instrumentation error.
Expected behavior
The Profiler should record render reasons without throwing an instrumentation error.
Minimal reproduction shape
const ControlledMemoInput = memo(
forwardRef(function ControlledMemoInput(
{ onChange, value },
forwardedRef
) {
const context = useContext(InputContext);
const inputRef = useRef(null);
const isFilled = useMemo(() => value.length > 0, [value]);
useEffect(() => {
if (typeof forwardedRef === "function") {
forwardedRef(inputRef.current);
}
}, [forwardedRef]);
return (
<input
ref={inputRef}
disabled={context.disabled}
onChange={onChange}
value={value}
data-filled={isFilled}
/>
);
})
);
function App() {
const [value, setValue] = useState("");
const [renderRevision, setRenderRevision] = useState(0);
useEffect(() => {
setRenderRevision(1);
}, []);
return (
<ControlledMemoInput
value={value}
onChange={(event) => setValue(event.target.value)}
/>
);
}
Investigation
The failure appears to be inside React DevToolsโ render-change inspection.
The profilerโs change-description path handles MemoComponent fibers and calls inspectHooks, but inspectHooksOfFiberImpl rejects the outer memo fiber with:
Unknown Fiber. Needs to be a function component to inspect hooks.
The relevant component is a memo(forwardRef(...)) Fiber, not a plain function component, simple memo component, or plain forward-ref component.
Workaround
Disable Record why each component rendered while profiling. Normal profiling continues to work, but render-reason information is unavailable.
How often does this bug happen?
Every time
DevTools package (automated)
No response
DevTools version (automated)
No response
Error message (automated)
No response
Error call stack (automated)
Error component stack (automated)
GitHub query string (automated)
Comments Threads Pending Resolution
Resolved Comment Threads
No resolved comments have been left on this PR.