@samitouri / QOS-React / commits / dde875dfb1

[Fizz] Implement Hooks (#21257)

* Implement Fizz Hooks This is pretty much just a copy of the partial renderer Hooks. * Implement forward ref and memo

Sebastian Markbåge committed Apr 14, 2021 at 17:16 UTC dde875dfb1f84692ed02f82b0fc781bec9a864ef
2 files changed +555 -56
packages/react-server/src/ReactFizzHooks.js new
+515
@@ -0,0 +1,515 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes';
11 +
12 +import type {
13 + MutableSource,
14 + MutableSourceGetSnapshotFn,
15 + MutableSourceSubscribeFn,
16 + ReactContext,
17 +} from 'shared/ReactTypes';
18 +
19 +import {readContext as readContextImpl} from './ReactFizzNewContext';
20 +
21 +import invariant from 'shared/invariant';
22 +import {enableCache} from 'shared/ReactFeatureFlags';
23 +import is from 'shared/objectIs';
24 +
25 +type BasicStateAction<S> = (S => S) | S;
26 +type Dispatch<A> = A => void;
27 +
28 +type Update<A> = {|
29 + action: A,
30 + next: Update<A> | null,
31 +|};
32 +
33 +type UpdateQueue<A> = {|
34 + last: Update<A> | null,
35 + dispatch: any,
36 +|};
37 +
38 +type Hook = {|
39 + memoizedState: any,
40 + queue: UpdateQueue<any> | null,
41 + next: Hook | null,
42 +|};
43 +
44 +type OpaqueIDType = string;
45 +
46 +let currentlyRenderingComponent: Object | null = null;
47 +let firstWorkInProgressHook: Hook | null = null;
48 +let workInProgressHook: Hook | null = null;
49 +// Whether the work-in-progress hook is a re-rendered hook
50 +let isReRender: boolean = false;
51 +// Whether an update was scheduled during the currently executing render pass.
52 +let didScheduleRenderPhaseUpdate: boolean = false;
53 +// Lazily created map of render-phase updates
54 +let renderPhaseUpdates: Map<UpdateQueue<any>, Update<any>> | null = null;
55 +// Counter to prevent infinite loops.
56 +let numberOfReRenders: number = 0;
57 +const RE_RENDER_LIMIT = 25;
58 +
59 +let isInHookUserCodeInDev = false;
60 +
61 +// In DEV, this is the name of the currently executing primitive hook
62 +let currentHookNameInDev: ?string;
63 +
64 +function resolveCurrentlyRenderingComponent(): Object {
65 + invariant(
66 + currentlyRenderingComponent !== null,
67 + 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
68 + ' one of the following reasons:\n' +
69 + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
70 + '2. You might be breaking the Rules of Hooks\n' +
71 + '3. You might have more than one copy of React in the same app\n' +
72 + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
73 + );
74 + if (__DEV__) {
75 + if (isInHookUserCodeInDev) {
76 + console.error(
77 + 'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' +
78 + 'You can only call Hooks at the top level of your React function. ' +
79 + 'For more information, see ' +
80 + 'https://reactjs.org/link/rules-of-hooks',
81 + );
82 + }
83 + }
84 + return currentlyRenderingComponent;
85 +}
86 +
87 +function areHookInputsEqual(
88 + nextDeps: Array<mixed>,
89 + prevDeps: Array<mixed> | null,
90 +) {
91 + if (prevDeps === null) {
92 + if (__DEV__) {
93 + console.error(
94 + '%s received a final argument during this render, but not during ' +
95 + 'the previous render. Even though the final argument is optional, ' +
96 + 'its type cannot change between renders.',
97 + currentHookNameInDev,
98 + );
99 + }
100 + return false;
101 + }
102 +
103 + if (__DEV__) {
104 + // Don't bother comparing lengths in prod because these arrays should be
105 + // passed inline.
106 + if (nextDeps.length !== prevDeps.length) {
107 + console.error(
108 + 'The final argument passed to %s changed size between renders. The ' +
109 + 'order and size of this array must remain constant.\n\n' +
110 + 'Previous: %s\n' +
111 + 'Incoming: %s',
112 + currentHookNameInDev,
113 + `[${nextDeps.join(', ')}]`,
114 + `[${prevDeps.join(', ')}]`,
115 + );
116 + }
117 + }
118 + for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
119 + if (is(nextDeps[i], prevDeps[i])) {
120 + continue;
121 + }
122 + return false;
123 + }
124 + return true;
125 +}
126 +
127 +function createHook(): Hook {
128 + if (numberOfReRenders > 0) {
129 + invariant(false, 'Rendered more hooks than during the previous render');
130 + }
131 + return {
132 + memoizedState: null,
133 + queue: null,
134 + next: null,
135 + };
136 +}
137 +
138 +function createWorkInProgressHook(): Hook {
139 + if (workInProgressHook === null) {
140 + // This is the first hook in the list
141 + if (firstWorkInProgressHook === null) {
142 + isReRender = false;
143 + firstWorkInProgressHook = workInProgressHook = createHook();
144 + } else {
145 + // There's already a work-in-progress. Reuse it.
146 + isReRender = true;
147 + workInProgressHook = firstWorkInProgressHook;
148 + }
149 + } else {
150 + if (workInProgressHook.next === null) {
151 + isReRender = false;
152 + // Append to the end of the list
153 + workInProgressHook = workInProgressHook.next = createHook();
154 + } else {
155 + // There's already a work-in-progress. Reuse it.
156 + isReRender = true;
157 + workInProgressHook = workInProgressHook.next;
158 + }
159 + }
160 + return workInProgressHook;
161 +}
162 +
163 +export function prepareToUseHooks(componentIdentity: Object): void {
164 + currentlyRenderingComponent = componentIdentity;
165 + if (__DEV__) {
166 + isInHookUserCodeInDev = false;
167 + }
168 +
169 + // The following should have already been reset
170 + // didScheduleRenderPhaseUpdate = false;
171 + // firstWorkInProgressHook = null;
172 + // numberOfReRenders = 0;
173 + // renderPhaseUpdates = null;
174 + // workInProgressHook = null;
175 +}
176 +
177 +export function finishHooks(
178 + Component: any,
179 + props: any,
180 + children: any,
181 + refOrContext: any,
182 +): any {
183 + // This must be called after every function component to prevent hooks from
184 + // being used in classes.
185 +
186 + while (didScheduleRenderPhaseUpdate) {
187 + // Updates were scheduled during the render phase. They are stored in
188 + // the `renderPhaseUpdates` map. Call the component again, reusing the
189 + // work-in-progress hooks and applying the additional updates on top. Keep
190 + // restarting until no more updates are scheduled.
191 + didScheduleRenderPhaseUpdate = false;
192 + numberOfReRenders += 1;
193 +
194 + // Start over from the beginning of the list
195 + workInProgressHook = null;
196 +
197 + children = Component(props, refOrContext);
198 + }
199 + resetHooksState();
200 + return children;
201 +}
202 +
203 +// Reset the internal hooks state if an error occurs while rendering a component
204 +export function resetHooksState(): void {
205 + if (__DEV__) {
206 + isInHookUserCodeInDev = false;
207 + }
208 +
209 + currentlyRenderingComponent = null;
210 + didScheduleRenderPhaseUpdate = false;
211 + firstWorkInProgressHook = null;
212 + numberOfReRenders = 0;
213 + renderPhaseUpdates = null;
214 + workInProgressHook = null;
215 +}
216 +
217 +function getCacheForType<T>(resourceType: () => T): T {
218 + // TODO: This should silently mark this as client rendered since it's not necesssarily
219 + // considered an error. It needs to work for things like Flight though.
220 + invariant(false, 'Not implemented.');
221 +}
222 +
223 +function readContext<T>(context: ReactContext<T>): T {
224 + if (__DEV__) {
225 + if (isInHookUserCodeInDev) {
226 + console.error(
227 + 'Context can only be read while React is rendering. ' +
228 + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' +
229 + 'In function components, you can read it directly in the function body, but not ' +
230 + 'inside Hooks like useReducer() or useMemo().',
231 + );
232 + }
233 + }
234 + return readContextImpl(context);
235 +}
236 +
237 +function useContext<T>(context: ReactContext<T>): T {
238 + if (__DEV__) {
239 + currentHookNameInDev = 'useContext';
240 + }
241 + resolveCurrentlyRenderingComponent();
242 + return readContextImpl(context);
243 +}
244 +
245 +function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
246 + // $FlowFixMe: Flow doesn't like mixed types
247 + return typeof action === 'function' ? action(state) : action;
248 +}
249 +
250 +export function useState<S>(
251 + initialState: (() => S) | S,
252 +): [S, Dispatch<BasicStateAction<S>>] {
253 + if (__DEV__) {
254 + currentHookNameInDev = 'useState';
255 + }
256 + return useReducer(
257 + basicStateReducer,
258 + // useReducer has a special case to support lazy useState initializers
259 + (initialState: any),
260 + );
261 +}
262 +
263 +export function useReducer<S, I, A>(
264 + reducer: (S, A) => S,
265 + initialArg: I,
266 + init?: I => S,
267 +): [S, Dispatch<A>] {
268 + if (__DEV__) {
269 + if (reducer !== basicStateReducer) {
270 + currentHookNameInDev = 'useReducer';
271 + }
272 + }
273 + currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
274 + workInProgressHook = createWorkInProgressHook();
275 + if (isReRender) {
276 + // This is a re-render. Apply the new render phase updates to the previous
277 + // current hook.
278 + const queue: UpdateQueue<A> = (workInProgressHook.queue: any);
279 + const dispatch: Dispatch<A> = (queue.dispatch: any);
280 + if (renderPhaseUpdates !== null) {
281 + // Render phase updates are stored in a map of queue -> linked list
282 + const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
283 + if (firstRenderPhaseUpdate !== undefined) {
284 + renderPhaseUpdates.delete(queue);
285 + let newState = workInProgressHook.memoizedState;
286 + let update = firstRenderPhaseUpdate;
287 + do {
288 + // Process this render phase update. We don't have to check the
289 + // priority because it will always be the same as the current
290 + // render's.
291 + const action = update.action;
292 + if (__DEV__) {
293 + isInHookUserCodeInDev = true;
294 + }
295 + newState = reducer(newState, action);
296 + if (__DEV__) {
297 + isInHookUserCodeInDev = false;
298 + }
299 + update = update.next;
300 + } while (update !== null);
301 +
302 + workInProgressHook.memoizedState = newState;
303 +
304 + return [newState, dispatch];
305 + }
306 + }
307 + return [workInProgressHook.memoizedState, dispatch];
308 + } else {
309 + if (__DEV__) {
310 + isInHookUserCodeInDev = true;
311 + }
312 + let initialState;
313 + if (reducer === basicStateReducer) {
314 + // Special case for `useState`.
315 + initialState =
316 + typeof initialArg === 'function'
317 + ? ((initialArg: any): () => S)()
318 + : ((initialArg: any): S);
319 + } else {
320 + initialState =
321 + init !== undefined ? init(initialArg) : ((initialArg: any): S);
322 + }
323 + if (__DEV__) {
324 + isInHookUserCodeInDev = false;
325 + }
326 + workInProgressHook.memoizedState = initialState;
327 + const queue: UpdateQueue<A> = (workInProgressHook.queue = {
328 + last: null,
329 + dispatch: null,
330 + });
331 + const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(
332 + null,
333 + currentlyRenderingComponent,
334 + queue,
335 + ): any));
336 + return [workInProgressHook.memoizedState, dispatch];
337 + }
338 +}
339 +
340 +function useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T {
341 + currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
342 + workInProgressHook = createWorkInProgressHook();
343 +
344 + const nextDeps = deps === undefined ? null : deps;
345 +
346 + if (workInProgressHook !== null) {
347 + const prevState = workInProgressHook.memoizedState;
348 + if (prevState !== null) {
349 + if (nextDeps !== null) {
350 + const prevDeps = prevState[1];
351 + if (areHookInputsEqual(nextDeps, prevDeps)) {
352 + return prevState[0];
353 + }
354 + }
355 + }
356 + }
357 +
358 + if (__DEV__) {
359 + isInHookUserCodeInDev = true;
360 + }
361 + const nextValue = nextCreate();
362 + if (__DEV__) {
363 + isInHookUserCodeInDev = false;
364 + }
365 + workInProgressHook.memoizedState = [nextValue, nextDeps];
366 + return nextValue;
367 +}
368 +
369 +function useRef<T>(initialValue: T): {|current: T|} {
370 + currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
371 + workInProgressHook = createWorkInProgressHook();
372 + const previousRef = workInProgressHook.memoizedState;
373 + if (previousRef === null) {
374 + const ref = {current: initialValue};
375 + if (__DEV__) {
376 + Object.seal(ref);
377 + }
378 + workInProgressHook.memoizedState = ref;
379 + return ref;
380 + } else {
381 + return previousRef;
382 + }
383 +}
384 +
385 +export function useLayoutEffect(
386 + create: () => (() => void) | void,
387 + inputs: Array<mixed> | void | null,
388 +) {
389 + if (__DEV__) {
390 + currentHookNameInDev = 'useLayoutEffect';
391 + console.error(
392 + 'useLayoutEffect does nothing on the server, because its effect cannot ' +
393 + "be encoded into the server renderer's output format. This will lead " +
394 + 'to a mismatch between the initial, non-hydrated UI and the intended ' +
395 + 'UI. To avoid this, useLayoutEffect should only be used in ' +
396 + 'components that render exclusively on the client. ' +
397 + 'See https://reactjs.org/link/uselayouteffect-ssr for common fixes.',
398 + );
399 + }
400 +}
401 +
402 +function dispatchAction<A>(
403 + componentIdentity: Object,
404 + queue: UpdateQueue<A>,
405 + action: A,
406 +) {
407 + invariant(
408 + numberOfReRenders < RE_RENDER_LIMIT,
409 + 'Too many re-renders. React limits the number of renders to prevent ' +
410 + 'an infinite loop.',
411 + );
412 +
413 + if (componentIdentity === currentlyRenderingComponent) {
414 + // This is a render phase update. Stash it in a lazily-created map of
415 + // queue -> linked list of updates. After this render pass, we'll restart
416 + // and apply the stashed updates on top of the work-in-progress hook.
417 + didScheduleRenderPhaseUpdate = true;
418 + const update: Update<A> = {
419 + action,
420 + next: null,
421 + };
422 + if (renderPhaseUpdates === null) {
423 + renderPhaseUpdates = new Map();
424 + }
425 + const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
426 + if (firstRenderPhaseUpdate === undefined) {
427 + renderPhaseUpdates.set(queue, update);
428 + } else {
429 + // Append the update to the end of the list.
430 + let lastRenderPhaseUpdate = firstRenderPhaseUpdate;
431 + while (lastRenderPhaseUpdate.next !== null) {
432 + lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
433 + }
434 + lastRenderPhaseUpdate.next = update;
435 + }
436 + } else {
437 + // This means an update has happened after the function component has
438 + // returned. On the server this is a no-op. In React Fiber, the update
439 + // would be scheduled for a future render.
440 + }
441 +}
442 +
443 +export function useCallback<T>(
444 + callback: T,
445 + deps: Array<mixed> | void | null,
446 +): T {
447 + return useMemo(() => callback, deps);
448 +}
449 +
450 +// TODO Decide on how to implement this hook for server rendering.
451 +// If a mutation occurs during render, consider triggering a Suspense boundary
452 +// and falling back to client rendering.
453 +function useMutableSource<Source, Snapshot>(
454 + source: MutableSource<Source>,
455 + getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
456 + subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
457 +): Snapshot {
458 + resolveCurrentlyRenderingComponent();
459 + return getSnapshot(source._source);
460 +}
461 +
462 +function useDeferredValue<T>(value: T): T {
463 + resolveCurrentlyRenderingComponent();
464 + return value;
465 +}
466 +
467 +function unsupportedStartTransition() {
468 + invariant(false, 'startTransition cannot be called during server rendering.');
469 +}
470 +
471 +function useTransition(): [(callback: () => void) => void, boolean] {
472 + resolveCurrentlyRenderingComponent();
473 + return [unsupportedStartTransition, false];
474 +}
475 +
476 +function useOpaqueIdentifier(): OpaqueIDType {
477 + throw new Error('Not yet implemented.');
478 +}
479 +
480 +function unsupportedRefresh() {
481 + invariant(false, 'Cache cannot be refreshed during server rendering.');
482 +}
483 +
484 +function useCacheRefresh(): <T>(?() => T, ?T) => void {
485 + return unsupportedRefresh;
486 +}
487 +
488 +function noop(): void {}
489 +
490 +export const Dispatcher: DispatcherType = {
491 + readContext,
492 + useContext,
493 + useMemo,
494 + useReducer,
495 + useRef,
496 + useState,
497 + useLayoutEffect,
498 + useCallback,
499 + // useImperativeHandle is not run in the server environment
500 + useImperativeHandle: noop,
501 + // Effects are not run in the server environment.
502 + useEffect: noop,
503 + // Debugging effect
504 + useDebugValue: noop,
505 + useDeferredValue,
506 + useTransition,
507 + useOpaqueIdentifier,
508 + // Subscriptions are not setup in a server environment.
509 + useMutableSource,
510 +};
511 +
512 +if (enableCache) {
513 + Dispatcher.getCacheForType = getCacheForType;
514 + Dispatcher.useCacheRefresh = useCacheRefresh;
515 +}
packages/react-server/src/ReactFizzServer.js
+40 -56
@@ -7,7 +7,6 @@
7 * @flow
8 */
9
10 -import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes';
10 import type {
11 Destination,
12 Chunk,
@@ -70,6 +69,12 @@ import {
69 pushProvider,
70 popProvider,
71 } from './ReactFizzNewContext';
72 +import {
73 + prepareToUseHooks,
74 + finishHooks,
75 + resetHooksState,
76 + Dispatcher,
77 +} from './ReactFizzHooks';
78
79 import {
80 getIteratorFn,
@@ -199,6 +204,8 @@ function defaultErrorHandler(error: mixed) {
204 console['error'](error); // Don't transform to our wrapper
205 }
206
207 +function noop(): void {}
208 +
209 export function createRequest(
210 children: ReactNodeList,
211 destination: Destination,
@@ -474,8 +481,10 @@ function renderWithHooks<Props, SecondArg>(
481 props: Props,
482 secondArg: SecondArg,
483 ): any {
477 - // TODO: Set up Hooks etc.
478 - const children = Component(props, secondArg);
484 + const componentIdentity = {};
485 + prepareToUseHooks(componentIdentity);
486 + const result = Component(props, secondArg);
487 + const children = finishHooks(Component, props, result, secondArg);
488 if (children === undefined) {
489 invalidRenderResult(Component);
490 }
@@ -719,13 +728,29 @@ function validateFunctionComponentInDev(Component: any): void {
728 }
729 }
730
731 +function resolveDefaultProps(Component: any, baseProps: Object): Object {
732 + if (Component && Component.defaultProps) {
733 + // Resolve default props. Taken from ReactElement
734 + const props = Object.assign({}, baseProps);
735 + const defaultProps = Component.defaultProps;
736 + for (const propName in defaultProps) {
737 + if (props[propName] === undefined) {
738 + props[propName] = defaultProps[propName];
739 + }
740 + }
741 + return props;
742 + }
743 + return baseProps;
744 +}
745 +
746 function renderForwardRef(
747 request: Request,
748 task: Task,
749 type: any,
750 props: Object,
751 + ref: any,
752 ): void {
728 - throw new Error('Not yet implemented element type.');
753 + renderWithHooks(request, task, type, props, ref);
754 }
755
756 function renderMemo(
@@ -733,8 +758,11 @@ function renderMemo(
758 task: Task,
759 type: any,
760 props: Object,
761 + ref: any,
762 ): void {
737 - throw new Error('Not yet implemented element type.');
763 + const innerType = type.type;
764 + const resolvedProps = resolveDefaultProps(innerType, props);
765 + renderElement(request, task, innerType, resolvedProps, ref);
766 }
767
768 function renderContextConsumer(
@@ -826,6 +854,7 @@ function renderElement(
854 task: Task,
855 type: any,
856 props: Object,
857 + ref: any,
858 ): void {
859 if (typeof type === 'function') {
860 if (shouldConstruct(type)) {
@@ -868,11 +897,11 @@ function renderElement(
897 if (typeof type === 'object' && type !== null) {
898 switch (type.$$typeof) {
899 case REACT_FORWARD_REF_TYPE: {
871 - renderForwardRef(request, task, type, props);
900 + renderForwardRef(request, task, type, props, ref);
901 return;
902 }
903 case REACT_MEMO_TYPE: {
875 - renderMemo(request, task, type, props);
904 + renderMemo(request, task, type, props, ref);
905 return;
906 }
907 case REACT_PROVIDER_TYPE: {
@@ -952,7 +981,8 @@ function renderNodeDestructive(
981 const element: React$Element<any> = (node: any);
982 const type = element.type;
983 const props = element.props;
955 - renderElement(request, task, type, props);
984 + const ref = element.ref;
985 + renderElement(request, task, type, props, ref);
986 return;
987 }
988 case REACT_PORTAL_TYPE:
@@ -1102,6 +1132,7 @@ function renderNode(request: Request, task: Task, node: ReactNodeList): void {
1132 try {
1133 return renderNodeDestructive(request, task, node);
1134 } catch (x) {
1135 + resetHooksState();
1136 if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
1137 spawnNewSuspendedTask(request, task, x);
1138 // Restore the context. We assume that this will be restored by the inner
@@ -1290,6 +1321,7 @@ function retryTask(request: Request, task: Task): void {
1321 segment.status = COMPLETED;
1322 finishedTask(request, task.blockedBoundary, segment);
1323 } catch (x) {
1324 + resetHooksState();
1325 if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
1326 // Something suspended again, let's pick it back up later.
1327 const ping = task.ping;
@@ -1704,51 +1736,3 @@ export function abort(request: Request): void {
1736 fatalError(request, error);
1737 }
1738 }
1707 -
1708 -function notYetImplemented(): void {
1709 - throw new Error('Not yet implemented.');
1710 -}
1711 -
1712 -function unsupportedRefresh() {
1713 - invariant(false, 'Cache cannot be refreshed during server rendering.');
1714 -}
1715 -
1716 -function unsupportedStartTransition() {
1717 - invariant(false, 'startTransition cannot be called during server rendering.');
1718 -}
1719 -
1720 -function noop(): void {}
1721 -
1722 -const Dispatcher: DispatcherType = {
1723 - useMemo<T>(nextCreate: () => T): T {
1724 - return nextCreate();
1725 - },
1726 - useCallback<T>(callback: T): T {
1727 - return callback;
1728 - },
1729 - useDebugValue(): void {},
1730 - useDeferredValue<T>(value: T): T {
1731 - return value;
1732 - },
1733 - useTransition(): [(callback: () => void) => void, boolean] {
1734 - return [unsupportedStartTransition, false];
1735 - },
1736 - getCacheForType<T>(resourceType: () => T): T {
1737 - throw new Error('Not yet implemented. Should mark as client rendered.');
1738 - },
1739 - readContext: (notYetImplemented: any),
1740 - useContext: (notYetImplemented: any),
1741 - useReducer: (notYetImplemented: any),
1742 - useRef: (notYetImplemented: any),
1743 - useState: (notYetImplemented: any),
1744 - useLayoutEffect: noop,
1745 - // useImperativeHandle is not run in the server environment
1746 - useImperativeHandle: noop,
1747 - // Effects are not run in the server environment.
1748 - useEffect: noop,
1749 - useOpaqueIdentifier: (notYetImplemented: any),
1750 - useMutableSource: (notYetImplemented: any),
1751 - useCacheRefresh(): <T>(?() => T, ?T) => void {
1752 - return unsupportedRefresh;
1753 - },
1754 -};