Support new Flare hook, unstable_useListener
Brian Vaughn committed
Jul 25, 2019 at 08:21 UTC
270f34f7f0e1d121c340937fe3302908ec117e3d
2 files changed
+106
-25
src/backend/ReactDebugHooks.js
+66
-25
@@ -8,6 +8,8 @@
8
9
import ErrorStackParser from 'error-stack-parser';
10
11
+import type { ReactContext, ReactEventResponder } from './types';
12
+
13
type Fiber = any;
14
type Hook = any;
15
@@ -28,6 +30,8 @@ type HookLogEntry = {
30
value: mixed,
31
};
32
33
+const emptyObject = {};
34
+
35
let hookLog: Array<HookLogEntry> = [];
36
37
// Primitives
@@ -53,9 +57,9 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
57
Dispatcher.useLayoutEffect(() => {});
58
Dispatcher.useEffect(() => {});
59
Dispatcher.useImperativeHandle(undefined, () => null);
60
+ Dispatcher.useDebugValue(null);
61
Dispatcher.useCallback(() => {});
62
Dispatcher.useMemo(() => null);
58
- Dispatcher.useDebugValue(null);
63
} finally {
64
readHookLog = hookLog;
65
hookLog = [];
@@ -79,14 +83,17 @@ function nextHook(): null | Hook {
83
}
84
85
function readContext<T>(
82
- context: any,
86
+ context: ReactContext<T>,
87
observedBits: void | number | boolean
88
): T {
89
// For now we don't expose readContext usage in the hooks debugging info.
90
return context._currentValue;
91
}
92
89
-function useContext<T>(context: any, observedBits: void | number | boolean): T {
93
+function useContext<T>(
94
+ context: ReactContext<T>,
95
+ observedBits: void | number | boolean
96
+): T {
97
hookLog.push({
98
primitive: 'Context',
99
stackError: new Error(),
@@ -109,13 +116,18 @@ function useState<S>(
116
return [state, (action: BasicStateAction<S>) => {}];
117
}
118
112
-function useReducer<S, A>(
119
+function useReducer<S, I, A>(
120
reducer: (S, A) => S,
114
- initialState: S,
115
- initialAction: A | void | null
121
+ initialArg: I,
122
+ init?: I => S
123
): [S, Dispatch<A>] {
124
const hook = nextHook();
118
- const state = hook !== null ? hook.memoizedState : initialState;
125
+ let state;
126
+ if (hook !== null) {
127
+ state = hook.memoizedState;
128
+ } else {
129
+ state = init !== undefined ? init(initialArg) : ((initialArg: any): S);
130
+ }
131
hookLog.push({
132
primitive: 'Reducer',
133
stackError: new Error(),
@@ -136,7 +148,7 @@ function useRef<T>(initialValue: T): { current: T } {
148
}
149
150
function useLayoutEffect(
139
- create: () => mixed,
151
+ create: () => (() => void) | void,
152
inputs: Array<mixed> | void | null
153
): void {
154
nextHook();
@@ -148,7 +160,7 @@ function useLayoutEffect(
160
}
161
162
function useEffect(
151
- create: () => mixed,
163
+ create: () => (() => void) | void,
164
inputs: Array<mixed> | void | null
165
): void {
166
nextHook();
@@ -176,22 +188,22 @@ function useImperativeHandle<T>(
188
});
189
}
190
179
-function useCallback<T>(callback: T, inputs: Array<mixed> | void | null): T {
180
- const hook = nextHook();
191
+function useDebugValue(value: any, formatterFn: ?(value: any) => any) {
192
hookLog.push({
182
- primitive: 'Callback',
193
+ primitive: 'DebugValue',
194
stackError: new Error(),
184
- value: hook !== null ? hook.memoizedState[0] : callback,
195
+ value: typeof formatterFn === 'function' ? formatterFn(value) : value,
196
});
186
- return callback;
197
}
198
189
-function useDebugValue(value: any, formatterFn: ?(value: any) => any) {
199
+function useCallback<T>(callback: T, inputs: Array<mixed> | void | null): T {
200
+ const hook = nextHook();
201
hookLog.push({
191
- primitive: 'DebugValue',
202
+ primitive: 'Callback',
203
stackError: new Error(),
193
- value: typeof formatterFn === 'function' ? formatterFn(value) : value,
204
+ value: hook !== null ? hook.memoizedState[0] : callback,
205
});
206
+ return callback;
207
}
208
209
function useMemo<T>(
@@ -204,6 +216,21 @@ function useMemo<T>(
216
return value;
217
}
218
219
+function useListener(
220
+ responder: ReactEventResponder<any, any>,
221
+ hookProps: ?Object
222
+): void {
223
+ const value = {
224
+ responder: responder.displayName || 'EventComponent',
225
+ props: hookProps || emptyObject,
226
+ };
227
+ hookLog.push({
228
+ primitive: 'Listener',
229
+ stackError: new Error(),
230
+ value,
231
+ });
232
+}
233
+
234
const Dispatcher = {
235
readContext,
236
useCallback,
@@ -216,6 +243,7 @@ const Dispatcher = {
243
useReducer,
244
useRef,
245
useState,
246
+ useListener,
247
};
248
249
// Inspect
@@ -400,10 +428,10 @@ function buildTree(rootStack, readHookLog): HooksTree {
428
for (let j = stack.length - commonSteps - 1; j >= 1; j--) {
429
const children = [];
430
levelChildren.push({
403
- name: parseCustomHookName(stack[j - 1].functionName),
404
- value: undefined,
431
id: null,
432
isStateEditable: false,
433
+ name: parseCustomHookName(stack[j - 1].functionName),
434
+ value: undefined,
435
subHooks: children,
436
});
437
stackOfChildren.push(levelChildren);
@@ -438,10 +466,11 @@ function buildTree(rootStack, readHookLog): HooksTree {
466
return rootChildren;
467
}
468
441
-// Custom hooks support user-configurable labels (via the useDebugValue() hook).
442
-// That hook adds the user-provided values to the hooks tree.
443
-// This method removes those values (so they don't appear in DevTools),
444
-// and bubbles them up to the "value" attribute of their parent custom hook.
469
+// Custom hooks support user-configurable labels (via the special useDebugValue() hook).
470
+// That hook adds user-provided values to the hooks tree,
471
+// but these values aren't intended to appear alongside of the other hooks.
472
+// Instead they should be attributed to their parent custom hook.
473
+// This method walks the tree and assigns debug values to their custom hook owners.
474
function processDebugValues(
475
hooksTree: HooksTree,
476
parentHooksNode: HooksNode | null
@@ -459,8 +488,8 @@ function processDebugValues(
488
}
489
}
490
462
- // Bubble debug value labels to their parent custom hook.
463
- // If there is no parent hook, just ignore them.
491
+ // Bubble debug value labels to their custom hook owner.
492
+ // If there is no parent hook, just ignore them for now.
493
// (We may warn about this in the future.)
494
if (parentHooksNode !== null) {
495
if (debugValueHooksNodes.length === 1) {
@@ -476,6 +505,12 @@ export function inspectHooks<Props>(
505
props: Props,
506
currentDispatcher: ReactCurrentDispatcher
507
): HooksTree {
508
+ // DevTools will pass the current renderer's injected dispatcher.
509
+ // Other apps might compile debug hooks as part of their app though.
510
+ //if (currentDispatcher == null) {
511
+ //currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
512
+ //}
513
+
514
const previousDispatcher = currentDispatcher.current;
515
let readHookLog;
516
currentDispatcher.current = Dispatcher;
@@ -554,6 +589,12 @@ export function inspectHooksOfFiber(
589
fiber: Fiber,
590
currentDispatcher: ReactCurrentDispatcher
591
) {
592
+ // DevTools will pass the current renderer's injected dispatcher.
593
+ // Other apps might compile debug hooks as part of their app though.
594
+ //if (currentDispatcher == null) {
595
+ //currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
596
+ //}
597
+
598
if (
599
fiber.tag !== FunctionComponent &&
600
fiber.tag !== SimpleMemoComponent &&
src/backend/types.js
+40
@@ -91,6 +91,27 @@ export type GetFiberIDForNative = (
91
) => number | null;
92
export type FindNativeNodesForFiberID = (id: number) => ?Array<NativeType>;
93
94
+export type ReactProviderType<T> = {
95
+ $$typeof: Symbol | number,
96
+ _context: ReactContext<T>,
97
+};
98
+
99
+export type ReactContext<T> = {
100
+ $$typeof: Symbol | number,
101
+ Consumer: ReactContext<T>,
102
+ Provider: ReactProviderType<T>,
103
+
104
+ _calculateChangedBits: ((a: T, b: T) => number) | null,
105
+
106
+ _currentValue: T,
107
+ _currentValue2: T,
108
+ _threadCount: number,
109
+
110
+ // DEV only
111
+ _currentRenderer?: Object | null,
112
+ _currentRenderer2?: Object | null,
113
+};
114
+
115
export type ReactRenderer = {
116
findFiberByHostInstance: (hostInstance: NativeType) => ?Fiber,
117
version: string,
@@ -332,3 +353,22 @@ export type HooksNode = {
353
subHooks: Array<HooksNode>,
354
};
355
export type HooksTree = Array<HooksNode>;
356
+
357
+export type ReactEventResponder<E, C> = {
358
+ $$typeof: Symbol | number,
359
+ displayName: string,
360
+ targetEventTypes: null | Array<string>,
361
+ rootEventTypes: null | Array<string>,
362
+ getInitialState: null | ((props: Object) => Object),
363
+ onEvent:
364
+ | null
365
+ | ((event: E, context: C, props: Object, state: Object) => void),
366
+ onRootEvent:
367
+ | null
368
+ | ((event: E, context: C, props: Object, state: Object) => void),
369
+ onMount: null | ((context: C, props: Object, state: Object) => void),
370
+ onUnmount: null | ((context: C, props: Object, state: Object) => void),
371
+ onOwnershipChange:
372
+ | null
373
+ | ((context: C, props: Object, state: Object) => void),
374
+};