import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {HooksNode, HooksTree} from 'react-debug-tools/src/ReactDebugHooks';
import type {WorkTagMap} from '../../types';
import {getFiberFlags} from './DevToolsFiberInspection';
import is from 'shared/objectIs';
export function getContextChanged(prevFiber: Fiber, nextFiber: Fiber): boolean {
let prevContext =
prevFiber.dependencies && prevFiber.dependencies.firstContext;
let nextContext =
nextFiber.dependencies && nextFiber.dependencies.firstContext;
while (prevContext && nextContext) {
if (prevContext.context !== nextContext.context) {
return false;
}
if (!is(prevContext.memoizedValue, nextContext.memoizedValue)) {
return true;
}
prevContext = prevContext.next;
nextContext = nextContext.next;
}
return false;
}
export function didStatefulHookChange(
prev: HooksNode,
next: HooksNode,
): boolean {
const isStatefulHook =
prev.isStateEditable === true ||
prev.name === 'SyncExternalStore' ||
prev.name === 'Transition' ||
prev.name === 'ActionState' ||
prev.name === 'FormState';
if (isStatefulHook) {
return prev.value !== next.value;
}
return false;
}
export function getChangedHooksIndices(
prevHooks: HooksTree | null,
nextHooks: HooksTree | null,
): null | Array<number> {
if (prevHooks == null || nextHooks == null) {
return null;
}
const indices: Array<number> = [];
let index = 0;
function traverse(prevTree: HooksTree, nextTree: HooksTree): void {
for (let i = 0; i < prevTree.length; i++) {
const prevHook = prevTree[i];
const nextHook = nextTree[i];
if (prevHook.subHooks.length > 0 && nextHook.subHooks.length > 0) {
traverse(prevHook.subHooks, nextHook.subHooks);
continue;
}
if (didStatefulHookChange(prevHook, nextHook)) {
indices.push(index);
}
index++;
}
}
traverse(prevHooks, nextHooks);
return indices;
}
export function getChangedKeys(prev: any, next: any): null | Array<string> {
if (prev == null || next == null) {
return null;
}
const keys = new Set([...Object.keys(prev), ...Object.keys(next)]);
const changedKeys = [];
for (const key of keys) {
if (prev[key] !== next[key]) {
changedKeys.push(key);
}
}
return changedKeys;
}
export function didFiberRender(
workTagMap: WorkTagMap,
prevFiber: Fiber,
nextFiber: Fiber,
): boolean {
switch (nextFiber.tag) {
case workTagMap.ClassComponent:
case workTagMap.FunctionComponent:
case workTagMap.ContextConsumer:
case workTagMap.MemoComponent:
case workTagMap.SimpleMemoComponent:
case workTagMap.ForwardRef:
const PerformedWork = 0b000000000000000000000000001;
return (getFiberFlags(nextFiber) & PerformedWork) === PerformedWork;
default:
return (
prevFiber.memoizedProps !== nextFiber.memoizedProps ||
prevFiber.memoizedState !== nextFiber.memoizedState ||
prevFiber.ref !== nextFiber.ref
);
}
}