@samitouri / QOS-React-2 / commits / 270f34f7f0

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 +};