bippy 0.7.3-dev.8ba0705 → 0.7.3-dev.c0e872e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/rdt-hook.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // This module must load before React so renderers can inject into the hook.
2
2
 
3
+ import { callListener } from "./call-listener.js";
3
4
  import type { FiberRoot, ReactDevToolsGlobalHook, ReactRenderer } from "./react-internals/index.js";
4
5
 
5
6
  export interface Unsubscribe extends Disposable {
@@ -23,6 +24,10 @@ interface RDTHookReplaceListener {
23
24
  (rdtHook: ReactDevToolsGlobalHook, target: ReactDevToolsTarget): void;
24
25
  }
25
26
 
27
+ interface RDTHookReplaceSubscription {
28
+ listener: RDTHookReplaceListener;
29
+ }
30
+
26
31
  export interface ReactDevToolsTarget {
27
32
  __REACT_DEVTOOLS_GLOBAL_HOOK__?: ReactDevToolsGlobalHook;
28
33
  }
@@ -49,6 +54,8 @@ export const isFiberRootUnmounted = (fiberRoot: FiberRoot): boolean => {
49
54
  };
50
55
 
51
56
  const checkDCE = (functionToCheck: unknown): void => {
57
+ // HACK: Node loads React's unbundled entrypoints, where development guards are intentionally not eliminated.
58
+ if (typeof process !== "undefined" && process.versions?.node) return;
52
59
  try {
53
60
  const code = Function.prototype.toString.call(functionToCheck);
54
61
  if (code.indexOf("^_^") > -1) {
@@ -91,7 +98,7 @@ export const _renderers = new Set<ReactRenderer>();
91
98
 
92
99
  const activeListenerTargets = new WeakMap<ActiveListener, Set<ReactDevToolsTarget>>();
93
100
  const rendererInjectSubscriptions = new Set<RendererInjectSubscription>();
94
- const rdtHookReplaceListeners = new Set<RDTHookReplaceListener>();
101
+ const rdtHookReplaceSubscriptions = new Set<RDTHookReplaceSubscription>();
95
102
  const notifiedRenderersByTarget = new WeakMap<ReactDevToolsTarget, WeakSet<ReactRenderer>>();
96
103
 
97
104
  const addActiveListener = (listener: ActiveListener, target: ReactDevToolsTarget): void => {
@@ -113,9 +120,12 @@ export const removeActiveListener = (
113
120
  }
114
121
  };
115
122
 
116
- const notifyActiveListeners = (target: ReactDevToolsTarget): void => {
117
- for (const listener of _onActiveListeners) {
118
- if (activeListenerTargets.get(listener)?.has(target)) listener();
123
+ const notifyActiveListeners = (
124
+ target: ReactDevToolsTarget,
125
+ pendingListeners = [..._onActiveListeners],
126
+ ): void => {
127
+ for (const listener of pendingListeners) {
128
+ if (activeListenerTargets.get(listener)?.has(target)) callListener(listener, undefined);
119
129
  }
120
130
  };
121
131
 
@@ -123,8 +133,11 @@ const notifyRendererInjectListeners = (
123
133
  target: ReactDevToolsTarget,
124
134
  renderer: ReactRenderer,
125
135
  ): void => {
126
- for (const subscription of rendererInjectSubscriptions) {
127
- if (subscription.target === target) subscription.listener(renderer);
136
+ const subscriptionSnapshot = [...rendererInjectSubscriptions];
137
+ for (const subscription of subscriptionSnapshot) {
138
+ if (rendererInjectSubscriptions.has(subscription) && subscription.target === target) {
139
+ callListener(subscription.listener, subscription, renderer);
140
+ }
128
141
  }
129
142
  };
130
143
 
@@ -132,8 +145,11 @@ const notifyRDTHookReplaceListeners = (
132
145
  rdtHook: ReactDevToolsGlobalHook,
133
146
  target: ReactDevToolsTarget,
134
147
  ): void => {
135
- for (const listener of rdtHookReplaceListeners) {
136
- listener(rdtHook, target);
148
+ const subscriptionSnapshot = [...rdtHookReplaceSubscriptions];
149
+ for (const subscription of subscriptionSnapshot) {
150
+ if (rdtHookReplaceSubscriptions.has(subscription)) {
151
+ callListener(subscription.listener, undefined, rdtHook, target);
152
+ }
137
153
  }
138
154
  };
139
155
 
@@ -150,9 +166,10 @@ export const onRendererInject = (
150
166
  };
151
167
 
152
168
  export const onRDTHookReplace = (listener: RDTHookReplaceListener): Unsubscribe => {
153
- rdtHookReplaceListeners.add(listener);
169
+ const subscription = { listener };
170
+ rdtHookReplaceSubscriptions.add(subscription);
154
171
  return createUnsubscribe(() => {
155
- rdtHookReplaceListeners.delete(listener);
172
+ rdtHookReplaceSubscriptions.delete(subscription);
156
173
  });
157
174
  };
158
175
 
@@ -240,10 +257,11 @@ export const installRDTHook = (
240
257
  _renderers.add(renderer);
241
258
  nextRenderers.set(rendererId, renderer);
242
259
  });
243
- if (ourRenderers.size > 0 || rdtHookReplaceListeners.size > 0) {
244
- patchRDTHook(onActive, target);
260
+ if (ourRenderers.size > 0 || rdtHookReplaceSubscriptions.size > 0) {
261
+ patchRDTHook(undefined, target, () => notifyRDTHookReplaceListeners(newHook, target));
262
+ } else {
263
+ notifyRDTHookReplaceListeners(newHook, target);
245
264
  }
246
- notifyRDTHookReplaceListeners(rdtHook, target);
247
265
  }
248
266
  },
249
267
  });
@@ -285,9 +303,10 @@ export const installRDTHook = (
285
303
  export const patchRDTHook = (
286
304
  onActive?: ActiveListener,
287
305
  target: ReactDevToolsTarget = globalThis,
306
+ onReady?: () => void,
288
307
  ): void => {
289
308
  if (onActive) addActiveListener(onActive, target);
290
- let didNotifyActiveListeners = false;
309
+ let shouldNotifyActiveListeners = false;
291
310
  const rdtHook = getTargetHook(target);
292
311
  if (!rdtHook) return;
293
312
  const renderers = getRendererMap(rdtHook);
@@ -303,28 +322,25 @@ export const patchRDTHook = (
303
322
  if (!isReactDevtools) {
304
323
  rdtHook.on = noOp;
305
324
  }
306
- if (renderers.size) {
307
- renderers.forEach((renderer) => _renderers.add(renderer));
308
- rdtHook._instrumentationIsActive = true;
309
- notifyActiveListeners(target);
310
- didNotifyActiveListeners = true;
311
- } else if (!isReactDevtools && isReactRefresh(rdtHook)) {
312
- // HACK: react-refresh's stub inject never records renderers, so a React app
313
- // that injected before bippy loaded is undetectable through the renderers map.
314
- // A react-refresh hook implies a dev renderer, so activate immediately.
315
- rdtHook._instrumentationIsActive = true;
316
- notifyActiveListeners(target);
317
- didNotifyActiveListeners = true;
318
- }
325
+ const shouldActivate = renderers.size > 0 || (!isReactDevtools && isReactRefresh(rdtHook));
319
326
  const previousInject = rdtHook.inject;
320
327
  rdtHook.inject = (renderer) => {
321
328
  const rendererId = previousInject.call(rdtHook, renderer);
322
329
  trackInjectedRenderer(rdtHook, target, renderers, rendererId, renderer);
323
330
  return rendererId;
324
331
  };
332
+ if (shouldActivate) {
333
+ renderers.forEach((renderer) => _renderers.add(renderer));
334
+ rdtHook._instrumentationIsActive = true;
335
+ shouldNotifyActiveListeners = true;
336
+ }
325
337
  }
326
- if (!didNotifyActiveListeners && (renderers.size || rdtHook._instrumentationIsActive)) {
327
- onActive?.();
338
+ const pendingListeners = shouldNotifyActiveListeners ? [..._onActiveListeners] : [];
339
+ onReady?.();
340
+ if (shouldNotifyActiveListeners) {
341
+ notifyActiveListeners(target, pendingListeners);
342
+ } else if (onActive && (renderers.size || rdtHook._instrumentationIsActive)) {
343
+ callListener(onActive, undefined);
328
344
  }
329
345
  };
330
346
 
@@ -0,0 +1,84 @@
1
+ // Adapted from ReactFiberTreeReflection.js. Copyright (c) Meta Platforms, Inc. and affiliates. MIT licensed.
2
+ import type { Fiber } from "./types.js";
3
+ import { getReactWorkTagsForFiber } from "./index.js";
4
+
5
+ export const getCurrentFiberFromRoot = (fiber: Fiber): Fiber | null => {
6
+ const alternate = fiber.alternate;
7
+ if (!alternate) return null;
8
+ let firstBranch = fiber;
9
+ let secondBranch = alternate;
10
+
11
+ while (firstBranch.return) {
12
+ const firstParent = firstBranch.return;
13
+ const secondParent = firstParent.alternate;
14
+ if (!secondParent) {
15
+ const nextParent = firstParent.return;
16
+ if (!nextParent) return null;
17
+ firstBranch = secondBranch = nextParent;
18
+ continue;
19
+ }
20
+
21
+ if (firstParent.child === secondParent.child) {
22
+ let child = firstParent.child;
23
+ while (child) {
24
+ if (child === firstBranch || child === secondBranch) {
25
+ let root = firstParent;
26
+ while (root.return) root = root.return;
27
+ if (root.tag !== getReactWorkTagsForFiber(root).HostRoot) return null;
28
+ return child === firstBranch ? fiber : alternate;
29
+ }
30
+ child = child.sibling;
31
+ }
32
+ return null;
33
+ }
34
+
35
+ if (firstBranch.return !== secondBranch.return) {
36
+ firstBranch = firstParent;
37
+ secondBranch = secondParent;
38
+ } else {
39
+ let didFindChild = false;
40
+ let child = firstParent.child;
41
+ while (child) {
42
+ if (child === firstBranch) {
43
+ firstBranch = firstParent;
44
+ secondBranch = secondParent;
45
+ didFindChild = true;
46
+ break;
47
+ }
48
+ if (child === secondBranch) {
49
+ firstBranch = secondParent;
50
+ secondBranch = firstParent;
51
+ didFindChild = true;
52
+ break;
53
+ }
54
+ child = child.sibling;
55
+ }
56
+ if (!didFindChild) {
57
+ child = secondParent.child;
58
+ while (child) {
59
+ if (child === firstBranch) {
60
+ firstBranch = secondParent;
61
+ secondBranch = firstParent;
62
+ didFindChild = true;
63
+ break;
64
+ }
65
+ if (child === secondBranch) {
66
+ firstBranch = firstParent;
67
+ secondBranch = secondParent;
68
+ didFindChild = true;
69
+ break;
70
+ }
71
+ child = child.sibling;
72
+ }
73
+ if (!didFindChild) return null;
74
+ }
75
+ }
76
+ if (firstBranch.alternate !== secondBranch) return null;
77
+ }
78
+
79
+ if (firstBranch.tag !== getReactWorkTagsForFiber(firstBranch).HostRoot) return null;
80
+ const root = firstBranch.stateNode;
81
+ if (typeof root !== "object" || root === null || !("current" in root)) return null;
82
+ if (root.current === firstBranch) return fiber;
83
+ return root.current === secondBranch ? alternate : null;
84
+ };
@@ -25,6 +25,9 @@ export const ReactSymbols = {
25
25
  DEPRECATED_ASYNC_MODE_SYMBOL_STRING: "Symbol(react.async_mode)",
26
26
  ELEMENT_SYMBOL_STRING: "Symbol(react.transitional.element)",
27
27
  LEGACY_ELEMENT_SYMBOL_STRING: "Symbol(react.element)",
28
+ STRICT_MODE_NUMBER: 60108,
29
+ STRICT_MODE_SYMBOL_DESCRIPTION: "react.strict_mode",
30
+ STRICT_MODE_SYMBOL_STRING: "Symbol(react.strict_mode)",
28
31
  } as const;
29
32
 
30
33
  export interface ReactWorkTagMap {
@@ -18,8 +18,24 @@ export type {
18
18
  } from "./generated/react-work-tags.js";
19
19
  export * from "./types.js";
20
20
 
21
+ interface InheritedWorkTags {
22
+ workTags: Readonly<ReactWorkTagMap>;
23
+ generation: number;
24
+ }
25
+
21
26
  const defaultReactWorkTags = getReactWorkTags();
22
27
  const fiberReactWorkTags = new WeakMap<Fiber, Readonly<ReactWorkTagMap>>();
28
+ const inheritedFiberWorkTags = new WeakMap<Fiber, InheritedWorkTags>();
29
+ let workTagGeneration = 0;
30
+
31
+ const getCachedWorkTags = (fiber: Fiber): Readonly<ReactWorkTagMap> | undefined => {
32
+ const assignedWorkTags = fiberReactWorkTags.get(fiber);
33
+ if (assignedWorkTags) return assignedWorkTags;
34
+ const inheritedWorkTags = inheritedFiberWorkTags.get(fiber);
35
+ return inheritedWorkTags?.generation === workTagGeneration
36
+ ? inheritedWorkTags.workTags
37
+ : undefined;
38
+ };
23
39
 
24
40
  // React's experimental channel historically reported "0.0.0-experimental-<sha>"
25
41
  // as the runtime version; those builds use modern work tags, not the 16.x rows
@@ -39,25 +55,36 @@ export const getReactWorkTagsForRenderer = (
39
55
 
40
56
  export const setReactWorkTagsForFiber = (fiber: Fiber, renderer?: ReactRenderer): void => {
41
57
  const workTags = getReactWorkTagsForRenderer(renderer);
58
+ if (
59
+ getCachedWorkTags(fiber) !== workTags ||
60
+ (fiber.alternate && getCachedWorkTags(fiber.alternate) !== workTags)
61
+ ) {
62
+ workTagGeneration++;
63
+ }
42
64
  fiberReactWorkTags.set(fiber, workTags);
43
65
  if (fiber.alternate) fiberReactWorkTags.set(fiber.alternate, workTags);
44
66
  };
45
67
 
46
68
  export const getReactWorkTagsForFiber = (fiber: Fiber): Readonly<ReactWorkTagMap> => {
47
- const cachedWorkTags = fiberReactWorkTags.get(fiber);
48
- if (cachedWorkTags) return cachedWorkTags;
49
-
50
- const traversedFibers: Fiber[] = [fiber];
51
- let rootFiber = fiber;
52
- while (rootFiber.return) {
53
- rootFiber = rootFiber.return;
54
- traversedFibers.push(rootFiber);
69
+ let workTags = getCachedWorkTags(fiber);
70
+ if (workTags) return workTags;
71
+ const traversedFibers: Fiber[] = [];
72
+ let ancestor = fiber;
73
+ while (!workTags) {
74
+ traversedFibers.push(ancestor);
75
+ const parent = ancestor.return;
76
+ if (!parent) {
77
+ workTags = inheritedFiberWorkTags.get(ancestor)?.workTags ?? defaultReactWorkTags;
78
+ break;
79
+ }
80
+ ancestor = parent;
81
+ workTags = getCachedWorkTags(ancestor);
55
82
  }
56
- const workTags = fiberReactWorkTags.get(rootFiber) ?? defaultReactWorkTags;
83
+ const inheritedWorkTags = { workTags, generation: workTagGeneration };
57
84
  for (const traversedFiber of traversedFibers) {
58
- fiberReactWorkTags.set(traversedFiber, workTags);
85
+ inheritedFiberWorkTags.set(traversedFiber, inheritedWorkTags);
59
86
  if (traversedFiber.alternate) {
60
- fiberReactWorkTags.set(traversedFiber.alternate, workTags);
87
+ inheritedFiberWorkTags.set(traversedFiber.alternate, inheritedWorkTags);
61
88
  }
62
89
  }
63
90
  return workTags;
package/src/react.ts CHANGED
@@ -1,77 +1,162 @@
1
1
  import "./install-hook-only.js";
2
- import * as React from "react";
3
- import { isFiber } from "./core.js";
2
+ import React from "react";
3
+ import { isFiber, traverseFiber } from "./core.js";
4
+ import { _renderers } from "./rdt-hook.js";
5
+ import { getCurrentFiberFromRoot } from "./react-internals/current-fiber.js";
4
6
  import type { Fiber } from "./react-internals/index.js";
5
7
 
6
8
  export type { Fiber } from "./react-internals/index.js";
7
9
 
8
- const preserveState = (state: undefined): undefined => state;
9
- const readEmptySnapshot = (): undefined => undefined;
10
- const unsubscribeFromEmptyStore = (): void => {};
11
- const subscribeToEmptyStore = (): (() => void) => unsubscribeFromEmptyStore;
12
- const useSyncExternalStore: unknown = Reflect.get(React, "useSyncExternalStore");
10
+ interface RenderMarker {
11
+ (state: undefined): undefined;
12
+ }
13
13
 
14
- const captureFiberFromHook = (useCaptureHook: () => void): Fiber | null => {
15
- const originalBind = Function.prototype.bind;
16
- let capturedFiber: Fiber | null = null;
17
- // HACK: React binds hook callbacks to the rendering Fiber in production but exposes no public API for it.
14
+ interface ReducerQueue {
15
+ lastRenderedReducer: unknown;
16
+ }
17
+
18
+ interface FiberCapture {
19
+ fiber: Fiber;
20
+ queue: ReducerQueue | null;
21
+ }
22
+
23
+ const isReducerQueue = (value: unknown): value is ReducerQueue =>
24
+ typeof value === "object" && value !== null && "lastRenderedReducer" in value;
25
+
26
+ const hasRenderMarker = (fiber: Fiber, renderMarker: RenderMarker): boolean => {
27
+ for (let hook = fiber.memoizedState; hook; hook = hook.next) {
28
+ if (Array.isArray(hook.memoizedState) && hook.memoizedState[0] === renderMarker) return true;
29
+ }
30
+ return false;
31
+ };
32
+
33
+ const getReducerQueue = (fiber: Fiber, renderMarker: RenderMarker): ReducerQueue | null => {
34
+ for (let hook = fiber.memoizedState; hook; hook = hook.next) {
35
+ if (isReducerQueue(hook.queue) && hook.queue.lastRenderedReducer === renderMarker)
36
+ return hook.queue;
37
+ }
38
+ return null;
39
+ };
40
+
41
+ const getDevToolsFiber = (renderMarker: RenderMarker): Fiber | null => {
42
+ for (const renderer of _renderers) {
43
+ try {
44
+ const fiber = renderer.getCurrentFiber?.();
45
+ if (fiber && isFiber(fiber) && hasRenderMarker(fiber, renderMarker)) return fiber;
46
+ } catch {}
47
+ }
48
+ return null;
49
+ };
50
+
51
+ const captureReducerFiber = (
52
+ renderMarker: RenderMarker,
53
+ shouldCapture: boolean,
54
+ ): FiberCapture | null => {
55
+ let originalBind: typeof Function.prototype.bind | undefined;
56
+ if (shouldCapture) {
57
+ try {
58
+ originalBind = Function.prototype.bind;
59
+ } catch {}
60
+ }
61
+ if (!originalBind) {
62
+ React.useReducer(renderMarker, undefined);
63
+ return null;
64
+ }
65
+
66
+ let capture: FiberCapture | null = null;
67
+ let isCapturing = true;
68
+ // HACK: Production React binds a new reducer's Fiber and queue; validate the private reducer marker, not argument positions.
18
69
  const bindProxy = new Proxy(originalBind, {
19
- apply: (bind, functionToBind, boundArguments) => {
20
- const fiber = boundArguments[1];
21
- if (!capturedFiber && isFiber(fiber)) {
22
- capturedFiber = fiber;
70
+ apply: (bind, callback, boundArguments) => {
71
+ if (isCapturing && !capture) {
72
+ try {
73
+ const queue = boundArguments.find(
74
+ (argument): argument is ReducerQueue =>
75
+ isReducerQueue(argument) && argument.lastRenderedReducer === renderMarker,
76
+ );
77
+ if (queue) {
78
+ const fiber = boundArguments.find(
79
+ (argument): argument is Fiber => isFiber(argument) && isFiber(argument.return),
80
+ );
81
+ if (fiber) capture = { fiber, queue };
82
+ }
83
+ } catch {}
23
84
  }
24
- return Reflect.apply(bind, functionToBind, boundArguments);
85
+ return Reflect.apply(bind, callback, boundArguments);
25
86
  },
26
87
  });
27
- Reflect.set(Function.prototype, "bind", bindProxy);
28
88
 
29
89
  try {
30
- useCaptureHook();
90
+ try {
91
+ Reflect.set(Function.prototype, "bind", bindProxy);
92
+ } catch {}
93
+ React.useReducer(renderMarker, undefined);
94
+ return capture;
31
95
  } finally {
32
- if (Function.prototype.bind === bindProxy) {
33
- Reflect.set(Function.prototype, "bind", originalBind);
34
- }
96
+ isCapturing = false;
97
+ capture = null;
98
+ try {
99
+ if (Function.prototype.bind === bindProxy) {
100
+ Reflect.set(Function.prototype, "bind", originalBind);
101
+ }
102
+ } catch {}
35
103
  }
36
-
37
- return capturedFiber;
38
- };
39
-
40
- const useExternalStoreCapture = (): void => {
41
- if (typeof useSyncExternalStore !== "function") return;
42
- Reflect.apply(useSyncExternalStore, React, [
43
- subscribeToEmptyStore,
44
- readEmptySnapshot,
45
- readEmptySnapshot,
46
- ]);
47
104
  };
48
105
 
49
- const useReducerCapture = (): void => {
50
- React.useReducer(preserveState, undefined);
106
+ // HACK: React <16.13 attaches hooks after render; the shared reducer queue validates the root-walk fallback.
107
+ const getRenderingFiberFromRoot = (
108
+ { fiber, queue }: FiberCapture,
109
+ renderMarker: RenderMarker,
110
+ ): Fiber | null => {
111
+ if (queue?.lastRenderedReducer !== renderMarker) return null;
112
+ let rootFiber = fiber;
113
+ while (rootFiber.return) rootFiber = rootFiber.return;
114
+ const root = rootFiber.stateNode;
115
+ if (typeof root !== "object" || root === null || !("current" in root) || !isFiber(root.current)) {
116
+ return null;
117
+ }
118
+ const renderingRoot = root.current.alternate;
119
+ const renderingFiber = getCurrentFiberFromRoot(fiber)?.alternate;
120
+ if (renderingFiber) {
121
+ let ancestor = renderingFiber;
122
+ while (ancestor.return) ancestor = ancestor.return;
123
+ if (ancestor === renderingRoot) return renderingFiber;
124
+ }
125
+ return traverseFiber(renderingRoot, (candidate) => {
126
+ if (candidate !== fiber && candidate !== fiber.alternate) return false;
127
+ let parent = candidate;
128
+ while (parent.return) parent = parent.return;
129
+ return parent === renderingRoot;
130
+ });
51
131
  };
52
132
 
53
- const useFiberWithExternalStore = (): Fiber | undefined =>
54
- captureFiberFromHook(useExternalStoreCapture) ?? undefined;
55
-
56
- const useFiberWithReducer = (): Fiber | undefined => {
57
- const committedFiberRef = React.useRef<Fiber | null>(null);
58
- const renderedFiberRef = React.useRef<Fiber | null>(null);
59
- const hookFiber = captureFiberFromHook(useReducerCapture);
133
+ export const useFiber = (): Fiber | undefined => {
134
+ "use no memo";
135
+ const fiberRef = React.useRef<FiberCapture | null>(null);
136
+ const renderMarker: RenderMarker = (state) => state;
137
+ React.useMemo(() => renderMarker, [renderMarker]);
138
+ const devToolsFiber = fiberRef.current ? null : getDevToolsFiber(renderMarker);
139
+ const capture = captureReducerFiber(renderMarker, !fiberRef.current && !devToolsFiber);
140
+ if (devToolsFiber) {
141
+ fiberRef.current = {
142
+ fiber: devToolsFiber,
143
+ queue: getReducerQueue(devToolsFiber, renderMarker),
144
+ };
145
+ return devToolsFiber;
146
+ }
147
+ const knownCapture = capture ?? fiberRef.current;
148
+ if (!knownCapture) return undefined;
149
+ const knownFiber = knownCapture.fiber;
60
150
  const fiber =
61
- hookFiber ??
62
- (renderedFiberRef.current !== committedFiberRef.current
63
- ? renderedFiberRef.current
64
- : committedFiberRef.current?.alternate) ??
65
- committedFiberRef.current;
66
-
67
- renderedFiberRef.current = fiber;
68
-
69
- React.useEffect(() => {
70
- committedFiberRef.current = fiber;
71
- }, [fiber]);
72
-
151
+ capture?.fiber ??
152
+ (hasRenderMarker(knownFiber, renderMarker)
153
+ ? knownFiber
154
+ : knownFiber.alternate && hasRenderMarker(knownFiber.alternate, renderMarker)
155
+ ? knownFiber.alternate
156
+ : getRenderingFiberFromRoot(knownCapture, renderMarker));
157
+ if (fiber) {
158
+ knownCapture.fiber = fiber;
159
+ fiberRef.current = knownCapture;
160
+ }
73
161
  return fiber ?? undefined;
74
162
  };
75
-
76
- export const useFiber =
77
- typeof useSyncExternalStore === "function" ? useFiberWithExternalStore : useFiberWithReducer;