Skip to content

Commit

Permalink
fix[ReactDebugHooks/find-primitive-index]: remove some assumptions (#…
Browse files Browse the repository at this point in the history
…29652)

Partially reverts #28593.

While rolling out RDT 5.2.0, I've observed some issues on React Native
side: hooks inspection for some complex hook trees, like in
AnimatedView, were broken. After some debugging, I've noticed a
difference between what is in frame's source.

The difference is in the top-most frame, where with V8 it will correctly
pick up the `Type` as `Proxy` in `hookStack`, but for Hermes it will be
`Object`. This means that for React Native this top most frame is
skipped, since sources are identical.

Here I am reverting back to the previous logic, where we check each
frame if its a part of the wrapper, but also updated `isReactWrapper`
function to have an explicit case for `useFormStatus` support.
  • Loading branch information
hoxyq committed May 30, 2024
1 parent 5bd4031 commit fb61a1b
Showing 1 changed file with 18 additions and 10 deletions.
28 changes: 18 additions & 10 deletions packages/react-debug-tools/src/ReactDebugHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,12 @@ function findCommonAncestorIndex(rootStack: any, hookStack: any) {
}

function isReactWrapper(functionName: any, wrapperName: string) {
return parseHookName(functionName) === wrapperName;
const hookName = parseHookName(functionName);
if (wrapperName === 'HostTransitionStatus') {
return hookName === wrapperName || hookName === 'FormStatus';
}

return hookName === wrapperName;
}

function findPrimitiveIndex(hookStack: any, hook: HookLogEntry) {
Expand All @@ -878,21 +883,24 @@ function findPrimitiveIndex(hookStack: any, hook: HookLogEntry) {
return -1;
}
for (let i = 0; i < primitiveStack.length && i < hookStack.length; i++) {
// Note: there is no guarantee that we will find the top-most primitive frame in the stack
// For React Native (uses Hermes), these source fields will be identical and skipped
if (primitiveStack[i].source !== hookStack[i].source) {
// If the next frame is a method from the dispatcher, we
// assume that the next frame after that is the actual public API call.
// This prohibits nesting dispatcher calls in hooks.
// If the next two frames are functions called `useX` then we assume that they're part of the
// wrappers that the React package or other packages adds around the dispatcher.
if (
i < hookStack.length - 1 &&
isReactWrapper(hookStack[i].functionName, hook.dispatcherHookName)
) {
i++;
}
if (
i < hookStack.length - 1 &&
isReactWrapper(hookStack[i].functionName, hook.dispatcherHookName)
) {
i++;
// Guard against the dispatcher call being inlined.
// At this point we wouldn't be able to recover the actual React Hook name.
if (i < hookStack.length - 1) {
i++;
}
}

return i;
}
}
Expand Down Expand Up @@ -1040,7 +1048,7 @@ function buildTree(
const levelChild: HooksNode = {
id,
isStateEditable,
name: name,
name,
value: hook.value,
subHooks: [],
debugInfo: debugInfo,
Expand Down

0 comments on commit fb61a1b

Please sign in to comment.