diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index 33786a41877b8..10ab1dbce19f2 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -1908,6 +1908,20 @@ export function attach( return false; } + function isUseSyncExternalStoreHook(hookObject: any): boolean { + const queue = hookObject.queue; + if (!queue) { + return false; + } + + const boundHasOwnProperty = hasOwnProperty.bind(queue); + return ( + boundHasOwnProperty('value') && + boundHasOwnProperty('getSnapshot') && + typeof queue.getSnapshot === 'function' + ); + } + function isHookThatCanScheduleUpdate(hookObject: any) { const queue = hookObject.queue; if (!queue) { @@ -1924,12 +1938,7 @@ export function attach( return true; } - // Detect useSyncExternalStore() - return ( - boundHasOwnProperty('value') && - boundHasOwnProperty('getSnapshot') && - typeof queue.getSnapshot === 'function' - ); + return isUseSyncExternalStoreHook(hookObject); } function didStatefulHookChange(prev: any, next: any): boolean { @@ -1950,13 +1959,23 @@ export function attach( const indices = []; let index = 0; + while (next !== null) { if (didStatefulHookChange(prev, next)) { indices.push(index); } + + // useSyncExternalStore creates 2 internal hooks, but we only count it as 1 user-facing hook + if (isUseSyncExternalStoreHook(next)) { + if (next.next !== null) { + next = next.next; + prev = prev.next; + } + } + + index++; next = next.next; prev = prev.next; - index++; } return indices;