@samitouri / QOS-React-1 / commits / fb61a1b515

fix[ReactDebugHooks/find-primitive-index]: remove some assumptions (#29652)

Partially reverts https://github.com/facebook/react/pull/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.

Ruslan Lesiutin committed May 30, 2024 at 16:11 UTC fb61a1b515c5ea0e31b0dac19184454a79c4baf1
1 file changed +18 -10
packages/react-debug-tools/src/ReactDebugHooks.js
+18 -10
@@ -868,7 +868,12 @@ function findCommonAncestorIndex(rootStack: any, hookStack: any) {
868 }
869
870 function isReactWrapper(functionName: any, wrapperName: string) {
871 - return parseHookName(functionName) === wrapperName;
871 + const hookName = parseHookName(functionName);
872 + if (wrapperName === 'HostTransitionStatus') {
873 + return hookName === wrapperName || hookName === 'FormStatus';
874 + }
875 +
876 + return hookName === wrapperName;
877 }
878
879 function findPrimitiveIndex(hookStack: any, hook: HookLogEntry) {
@@ -878,21 +883,24 @@ function findPrimitiveIndex(hookStack: any, hook: HookLogEntry) {
883 return -1;
884 }
885 for (let i = 0; i < primitiveStack.length && i < hookStack.length; i++) {
886 + // Note: there is no guarantee that we will find the top-most primitive frame in the stack
887 + // For React Native (uses Hermes), these source fields will be identical and skipped
888 if (primitiveStack[i].source !== hookStack[i].source) {
882 - // If the next frame is a method from the dispatcher, we
883 - // assume that the next frame after that is the actual public API call.
884 - // This prohibits nesting dispatcher calls in hooks.
889 + // If the next two frames are functions called `useX` then we assume that they're part of the
890 + // wrappers that the React package or other packages adds around the dispatcher.
891 + if (
892 + i < hookStack.length - 1 &&
893 + isReactWrapper(hookStack[i].functionName, hook.dispatcherHookName)
894 + ) {
895 + i++;
896 + }
897 if (
898 i < hookStack.length - 1 &&
899 isReactWrapper(hookStack[i].functionName, hook.dispatcherHookName)
900 ) {
901 i++;
890 - // Guard against the dispatcher call being inlined.
891 - // At this point we wouldn't be able to recover the actual React Hook name.
892 - if (i < hookStack.length - 1) {
893 - i++;
894 - }
902 }
903 +
904 return i;
905 }
906 }
@@ -1040,7 +1048,7 @@ function buildTree(
1048 const levelChild: HooksNode = {
1049 id,
1050 isStateEditable,
1043 - name: name,
1051 + name,
1052 value: hook.value,
1053 subHooks: [],
1054 debugInfo: debugInfo,