@samitouri / QOS-React / commits / 8308d2f1fe

fix[react-devtools/ReactDebugHooks]: support unstable prefixes in hooks and useContextWithBailout (#30837)

Related - https://github.com/facebook/react/pull/30407. This is experimental-only and FB-only hook. Without these changes, inspecting an element that is using this hook will throw an error, because this hook is missing in Dispatcher implementation from React DevTools, which overrides the original one to build the hook tree. ![Screenshot 2024-08-28 at 18 42 55](https://github.com/user-attachments/assets/e3bccb92-74fb-4e4a-8181-03d13f8512c0) One nice thing from it is that in case of any potential regressions related to this experiment, we can quickly triage which implementation of `useContext` is used by inspecting an element in React DevTools. Ideally, I should've added some component that is using this hook to `react-devtools-shell`, so it can be manually tested, but I can't do it without rewriting the infra for it. This is because this hook is only available from fb-www builds, and not experimental.

Ruslan Lesiutin committed Aug 30, 2024 at 10:34 UTC 8308d2f1fe90ec0b5a5cde147b97c6e78581710a
1 file changed +30 -1
packages/react-debug-tools/src/ReactDebugHooks.js
+30 -1
@@ -20,6 +20,7 @@ import type {
20 Dependencies,
21 Fiber,
22 Dispatcher as DispatcherType,
23 + ContextDependencyWithSelect,
24 } from 'react-reconciler/src/ReactInternalTypes';
25 import type {TransitionStatus} from 'react-reconciler/src/ReactFiberConfig';
26
@@ -37,7 +38,6 @@ import {
38 REACT_CONTEXT_TYPE,
39 } from 'shared/ReactSymbols';
40 import hasOwnProperty from 'shared/hasOwnProperty';
40 -import type {ContextDependencyWithSelect} from '../../react-reconciler/src/ReactInternalTypes';
41
42 type CurrentDispatcherRef = typeof ReactSharedInternals;
43
@@ -76,6 +76,13 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
76 try {
77 // Use all hooks here to add them to the hook log.
78 Dispatcher.useContext(({_currentValue: null}: any));
79 + if (typeof Dispatcher.unstable_useContextWithBailout === 'function') {
80 + // This type check is for Flow only.
81 + Dispatcher.unstable_useContextWithBailout(
82 + ({_currentValue: null}: any),
83 + null,
84 + );
85 + }
86 Dispatcher.useState(null);
87 Dispatcher.useReducer((s: mixed, a: mixed) => s, null);
88 Dispatcher.useRef(null);
@@ -280,6 +287,22 @@ function useContext<T>(context: ReactContext<T>): T {
287 return value;
288 }
289
290 +function unstable_useContextWithBailout<T>(
291 + context: ReactContext<T>,
292 + select: (T => Array<mixed>) | null,
293 +): T {
294 + const value = readContext(context);
295 + hookLog.push({
296 + displayName: context.displayName || null,
297 + primitive: 'ContextWithBailout',
298 + stackError: new Error(),
299 + value: value,
300 + debugInfo: null,
301 + dispatcherHookName: 'ContextWithBailout',
302 + });
303 + return value;
304 +}
305 +
306 function useState<S>(
307 initialState: (() => S) | S,
308 ): [S, Dispatch<BasicStateAction<S>>] {
@@ -753,6 +776,7 @@ const Dispatcher: DispatcherType = {
776 useCacheRefresh,
777 useCallback,
778 useContext,
779 + unstable_useContextWithBailout,
780 useEffect,
781 useImperativeHandle,
782 useDebugValue,
@@ -954,6 +978,11 @@ function parseHookName(functionName: void | string): string {
978 } else {
979 startIndex += 1;
980 }
981 +
982 + if (functionName.slice(startIndex).startsWith('unstable_')) {
983 + startIndex += 'unstable_'.length;
984 + }
985 +
986 if (functionName.slice(startIndex, startIndex + 3) === 'use') {
987 if (functionName.length - startIndex === 3) {
988 return 'Use';