From f75b5d349e09e11f42d799938dc6cff57ea0ef6c Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Feb 2022 16:06:48 -0500 Subject: [PATCH 01/83] Flight side of server context --- .../react-client/src/ReactFlightClient.js | 29 ++- .../src/__tests__/ReactFlight-test.js | 198 ++++++++++++++ .../react-debug-tools/src/ReactDebugHooks.js | 18 +- .../src/backend/types.js | 4 +- .../src/server/ReactPartialRendererContext.js | 4 +- .../src/server/ReactPartialRendererHooks.js | 19 +- .../src/ReactNoopFlightClient.js | 3 +- .../src/ReactNoopFlightServer.js | 13 +- .../src/ReactFiberBeginWork.new.js | 9 +- .../src/ReactFiberBeginWork.old.js | 9 +- .../src/ReactFiberHooks.new.js | 70 ++++- .../src/ReactFiberHooks.old.js | 70 ++++- .../src/ReactFiberNewContext.new.js | 38 +-- .../src/ReactFiberNewContext.old.js | 37 +-- .../src/ReactFiberScope.new.js | 13 +- .../src/ReactFiberScope.old.js | 13 +- .../src/ReactFiberWorkLoop.new.js | 1 - .../src/ReactInternalTypes.js | 16 +- .../ReactFlightDOMRelayServerHostConfig.js | 37 +-- .../src/ReactFlightDOMServerBrowser.js | 17 +- .../src/ReactFlightDOMServerNode.js | 17 +- .../ReactFlightNativeRelayServerHostConfig.js | 5 +- packages/react-server/src/ReactFizzHooks.js | 17 +- .../react-server/src/ReactFizzNewContext.js | 18 +- packages/react-server/src/ReactFlightHooks.js | 113 ++++++++ .../react-server/src/ReactFlightNewContext.js | 246 ++++++++++++++++++ .../react-server/src/ReactFlightServer.js | 190 +++++++++----- .../src/ReactFlightServerConfigStream.js | 5 +- packages/react/index.classic.fb.js | 4 +- packages/react/index.experimental.js | 4 +- packages/react/index.js | 2 + packages/react/index.modern.fb.js | 4 +- packages/react/index.stable.js | 4 +- packages/react/src/React.js | 4 + packages/react/src/ReactHooks.js | 10 + packages/react/src/ReactServerContext.js | 63 +++++ packages/shared/ReactTypes.js | 30 ++- packages/shared/getComponentNameFromType.js | 2 +- scripts/error-codes/codes.json | 3 +- 39 files changed, 1145 insertions(+), 214 deletions(-) create mode 100644 packages/react-server/src/ReactFlightHooks.js create mode 100644 packages/react-server/src/ReactFlightNewContext.js create mode 100644 packages/react/src/ReactServerContext.js diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 9d5ac3680a8..acc3436f7c4 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -24,7 +24,13 @@ import { parseModel, } from './ReactFlightClientHostConfig'; -import {REACT_LAZY_TYPE, REACT_ELEMENT_TYPE} from 'shared/ReactSymbols'; +import {getOrCreateContextByName} from 'react/src/ReactServerContext'; + +import { + REACT_LAZY_TYPE, + REACT_ELEMENT_TYPE, + REACT_PROVIDER_TYPE, +} from 'shared/ReactSymbols'; export type JSONValue = | number @@ -318,6 +324,11 @@ export function parseModelString( // When passed into React, we'll know how to suspend on this. return createLazyChunkWrapper(chunk); } + case '!': { + if (value === '!') { + return REACT_PROVIDER_TYPE; + } + } } return value; } @@ -327,10 +338,18 @@ export function parseModelTuple( value: {+[key: string]: JSONValue} | $ReadOnlyArray, ): any { const tuple: [mixed, mixed, mixed, mixed] = (value: any); - if (tuple[0] === REACT_ELEMENT_TYPE) { - // TODO: Consider having React just directly accept these arrays as elements. - // Or even change the ReactElement type to be an array. - return createElement(tuple[1], tuple[2], tuple[3]); + + switch (tuple[0]) { + case REACT_ELEMENT_TYPE: + // TODO: Consider having React just directly accept these arrays as elements. + // Or even change the ReactElement type to be an array. + return createElement(tuple[1], tuple[2], tuple[3]); + case REACT_PROVIDER_TYPE: + return createElement( + getOrCreateContextByName((tuple[1]: any)).Provider, + tuple[2], + tuple[3], + ); } return value; } diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 8d0fbe1609d..2db829fd7cd 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -17,6 +17,7 @@ let ReactNoopFlightServer; let ReactNoopFlightClient; let ErrorBoundary; let NoErrorExpected; +let Scheduler; describe('ReactFlight', () => { beforeEach(() => { @@ -27,6 +28,7 @@ describe('ReactFlight', () => { ReactNoopFlightServer = require('react-noop-renderer/flight-server'); ReactNoopFlightClient = require('react-noop-renderer/flight-client'); act = require('jest-react').act; + Scheduler = require('scheduler'); ErrorBoundary = class extends React.Component { state = {hasError: false, error: null}; @@ -302,4 +304,200 @@ describe('ReactFlight', () => { {withoutStack: true}, ); }); + + it('supports basic createServerContext usage', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'hello from server', + ); + function Foo() { + const context = React.useServerContext(ServerContext); + return
{context}
; + } + + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(
hello from server
); + }); + + it('propagates ServerContext providers in flight', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + + function Foo() { + return ( +
+ + + +
+ ); + } + function Bar() { + const context = React.useServerContext(ServerContext); + return context; + } + + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); + }); + + it('propagates ServerContext and cleansup providers in flight', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + + function Foo() { + return ( + <> + + + + + + + + + + + + + + + ); + } + function Bar() { + const context = React.useServerContext(ServerContext); + return {context}; + } + + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput( + <> + hi this is server + hi this is server2 + hi this is server outer + hi this is server outer2 + default hello from server + , + ); + }); + + it('propagates ServerContext providers in flight after suspending', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + + function Foo() { + return ( +
+ + + + + +
+ ); + } + + let resolve; + const promise = new Promise(res => { + resolve = () => { + promise.unsuspend = true; + res(); + }; + }); + + function Bar() { + if (!promise.unsuspend) { + Scheduler.unstable_yieldValue('suspended'); + throw promise; + } + Scheduler.unstable_yieldValue('rendered'); + const context = React.useServerContext(ServerContext); + return context; + } + + const transport = ReactNoopFlightServer.render(); + + expect(Scheduler).toHaveYielded(['suspended']); + + await act(async () => { + resolve(); + await promise; + jest.runAllImmediates(); + }); + + expect(Scheduler).toHaveYielded(['rendered']); + + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); + }); + + it('serializes ServerContext to client', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + + function ClientBar() { + Scheduler.unstable_yieldValue('ClientBar'); + const context = React.useServerContext(ServerContext); + return {context}; + } + + const Bar = moduleReference(ClientBar); + + function Foo() { + return ( + + + + ); + } + + const model = { + foo: , + }; + + const transport = ReactNoopFlightServer.render(model); + + expect(Scheduler).toHaveYielded([]); + + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + const flightModel = ReactNoopFlightClient.read(transport); + ReactNoop.render(flightModel.foo); + }); + + expect(Scheduler).toHaveYielded(['ClientBar']); + expect(ReactNoop).toMatchRenderedOutput(hi this is server); + }); }); diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 895e7535986..4e248371ece 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -12,7 +12,9 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, ReactProviderType, + ServerContextJSONValue, } from 'shared/ReactTypes'; import type { Fiber, @@ -103,7 +105,9 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext(context: ReactContext): T { +function readContext( + context: ReactContext | ReactServerContext, +): T { // For now we don't expose readContext usage in the hooks debugging info. return context._currentValue; } @@ -117,6 +121,17 @@ function useContext(context: ReactContext): T { return context._currentValue; } +function useServerContext( + context: ReactServerContext, +): T { + hookLog.push({ + primitive: 'ServerContext', + stackError: new Error(), + value: context._currentValue, + }); + return context._currentValue; +} + function useState( initialState: (() => S) | S, ): [S, Dispatch>] { @@ -343,6 +358,7 @@ const Dispatcher: DispatcherType = { useMemo, useReducer, useRef, + useServerContext, useState, useTransition, useMutableSource, diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index 91a1a0c713c..28fa7f5a75c 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -82,9 +82,9 @@ export type GetFiberIDForNative = ( ) => number | null; export type FindNativeNodesForFiberID = (id: number) => ?Array; -export type ReactProviderType = { +export type ReactProviderType = { $$typeof: Symbol | number, - _context: ReactContext, + _context: ReactContext | ReactServerContext, ... }; diff --git a/packages/react-dom/src/server/ReactPartialRendererContext.js b/packages/react-dom/src/server/ReactPartialRendererContext.js index 65af591e365..bdcd9f4ba65 100644 --- a/packages/react-dom/src/server/ReactPartialRendererContext.js +++ b/packages/react-dom/src/server/ReactPartialRendererContext.js @@ -8,7 +8,7 @@ */ import type {ThreadID} from './ReactThreadIDAllocator'; -import type {ReactContext} from 'shared/ReactTypes'; +import type {ReactContext, ReactServerContext} from 'shared/ReactTypes'; import {disableLegacyContext} from 'shared/ReactFeatureFlags'; import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols'; @@ -44,7 +44,7 @@ function checkContextTypes(typeSpecs, values, location: string) { } export function validateContextBounds( - context: ReactContext, + context: ReactContext | ReactServerContext, threadID: ThreadID, ) { // If we don't have enough slots in this context to store this threadID, diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 2940c47bde4..1da0127a131 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -14,6 +14,8 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, + ServerContextValue, } from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; @@ -222,7 +224,9 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext(context: ReactContext): T { +function readContext( + context: ReactContext | ReactServerContext, +): T { const threadID = currentPartialRenderer.threadID; validateContextBounds(context, threadID); if (__DEV__) { @@ -248,6 +252,18 @@ function useContext(context: ReactContext): T { return context[threadID]; } +function useServerContext( + context: ReactServerContext, +): T { + if (__DEV__) { + currentHookNameInDev = 'useContext'; + } + resolveCurrentlyRenderingComponent(); + const threadID = currentPartialRenderer.threadID; + validateContextBounds(context, threadID); + return context[threadID]; +} + function basicStateReducer(state: S, action: BasicStateAction): S { // $FlowFixMe: Flow doesn't like mixed types return typeof action === 'function' ? action(state) : action; @@ -531,6 +547,7 @@ export const Dispatcher: DispatcherType = { useReducer, useRef, useState, + useServerContext, useInsertionEffect, useLayoutEffect, useCallback, diff --git a/packages/react-noop-renderer/src/ReactNoopFlightClient.js b/packages/react-noop-renderer/src/ReactNoopFlightClient.js index df586c6efb2..b4e2e830fc8 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightClient.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightClient.js @@ -40,7 +40,8 @@ function read(source: Source): T { processStringChunk(response, source[i], 0); } close(response); - return response.readRoot(); + const root = response.readRoot(); + return root; } export {read}; diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index f6d64c003f9..ae09467430d 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -27,8 +27,9 @@ const ReactNoopFlightServer = ReactFlightServer({ callback(); }, beginWriting(destination: Destination): void {}, - writeChunk(destination: Destination, chunk: string): void { + writeChunk(destination: Destination, chunk: string): boolean { destination.push(chunk); + return true; }, completeWriting(destination: Destination): void {}, close(destination: Destination): void {}, @@ -54,8 +55,16 @@ const ReactNoopFlightServer = ReactFlightServer({ }, }); +type ServerContextJSONValue = + | string + | boolean + | number + | null + | $ReadOnlyArray; + type Options = { onError?: (error: mixed) => void, + context?: Array<{name: string, value: ServerContextJSONValue}>, }; function render(model: ReactModel, options?: Options): Destination { @@ -64,7 +73,7 @@ function render(model: ReactModel, options?: Options): Destination { const request = ReactNoopFlightServer.createRequest( model, bundlerConfig, - options ? options.onError : undefined, + options, ); ReactNoopFlightServer.startWork(request); ReactNoopFlightServer.startFlowing(request, destination); diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index b001088f17e..6cf1c3f0e7e 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactProviderType, ReactContext} from 'shared/ReactTypes'; +import type { + ReactProviderType, + ReactContext, + ReactServerContext, +} from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {TypeOfMode} from './ReactTypeOfMode'; @@ -3170,7 +3174,8 @@ function updateContextProvider( renderLanes: Lanes, ) { const providerType: ReactProviderType = workInProgress.type; - const context: ReactContext = providerType._context; + const context: ReactContext | ReactServerContext = + providerType._context; const newProps = workInProgress.pendingProps; const oldProps = workInProgress.memoizedProps; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index cbf3c5fa2e3..dec9cbb7c32 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactProviderType, ReactContext} from 'shared/ReactTypes'; +import type { + ReactProviderType, + ReactContext, + ReactServerContext, +} from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {TypeOfMode} from './ReactTypeOfMode'; @@ -3170,7 +3174,8 @@ function updateContextProvider( renderLanes: Lanes, ) { const providerType: ReactProviderType = workInProgress.type; - const context: ReactContext = providerType._context; + const context: ReactContext | ReactServerContext = + providerType._context; const newProps = workInProgress.pendingProps; const oldProps = workInProgress.memoizedProps; diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 7747a25c858..a66ce4a9c3f 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -12,6 +12,8 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, + ServerContextJSONValue, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.new'; @@ -2375,6 +2377,7 @@ export const ContextOnlyDispatcher: Dispatcher = { useCallback: throwInvalidHookError, useContext: throwInvalidHookError, + useServerContext: throwInvalidHookError, useEffect: throwInvalidHookError, useImperativeHandle: throwInvalidHookError, useInsertionEffect: throwInvalidHookError, @@ -2403,6 +2406,7 @@ const HooksDispatcherOnMount: Dispatcher = { useCallback: mountCallback, useContext: readContext, + useServerContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, useLayoutEffect: mountLayoutEffect, @@ -2431,6 +2435,7 @@ const HooksDispatcherOnUpdate: Dispatcher = { useCallback: updateCallback, useContext: readContext, + useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2459,6 +2464,7 @@ const HooksDispatcherOnRerender: Dispatcher = { useCallback: updateCallback, useContext: readContext, + useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2510,7 +2516,7 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2524,6 +2530,13 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + mountHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2658,7 +2671,7 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2671,6 +2684,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2800,7 +2820,7 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2813,6 +2833,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2942,7 +2969,7 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, @@ -2956,6 +2983,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3085,7 +3119,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3101,6 +3135,14 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3244,7 +3286,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3260,6 +3302,14 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3420,6 +3470,14 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index d7137e8cd7f..72748ac26d6 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -12,6 +12,8 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, + ServerContextJSONValue, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.old'; @@ -2375,6 +2377,7 @@ export const ContextOnlyDispatcher: Dispatcher = { useCallback: throwInvalidHookError, useContext: throwInvalidHookError, + useServerContext: throwInvalidHookError, useEffect: throwInvalidHookError, useImperativeHandle: throwInvalidHookError, useInsertionEffect: throwInvalidHookError, @@ -2403,6 +2406,7 @@ const HooksDispatcherOnMount: Dispatcher = { useCallback: mountCallback, useContext: readContext, + useServerContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, useLayoutEffect: mountLayoutEffect, @@ -2431,6 +2435,7 @@ const HooksDispatcherOnUpdate: Dispatcher = { useCallback: updateCallback, useContext: readContext, + useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2459,6 +2464,7 @@ const HooksDispatcherOnRerender: Dispatcher = { useCallback: updateCallback, useContext: readContext, + useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2510,7 +2516,7 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2524,6 +2530,13 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + mountHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2658,7 +2671,7 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2671,6 +2684,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2800,7 +2820,7 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2813,6 +2833,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2942,7 +2969,7 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, @@ -2956,6 +2983,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3085,7 +3119,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3101,6 +3135,14 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3244,7 +3286,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3260,6 +3302,14 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3420,6 +3470,14 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 8ff30c810f0..e73158d2f77 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; +import type { + ReactContext, + ReactServerContext, + ReactProviderType, +} from 'shared/ReactTypes'; import type { Fiber, ContextDependency, @@ -83,11 +87,12 @@ export function exitDisallowedContextReadInDEV(): void { } } -export function pushProvider( +export function pushProvider( providerFiber: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, nextValue: T, ): void { + debugger; if (isPrimaryRenderer) { push(valueCursor, context._currentValue, providerFiber); @@ -125,16 +130,16 @@ export function pushProvider( } } -export function popProvider( - context: ReactContext, +export function popProvider( + context: ReactContext | ReactServerContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = currentValue; + context._currentValue = (currentValue: any); } else { - context._currentValue2 = currentValue; + context._currentValue2 = (currentValue: any); } } @@ -180,9 +185,9 @@ export function scheduleContextWorkOnParentPath( } } -export function propagateContextChange( +export function propagateContextChange( workInProgress: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, renderLanes: Lanes, ): void { if (enableLazyContextPropagation) { @@ -201,9 +206,9 @@ export function propagateContextChange( } } -function propagateContextChange_eager( +function propagateContextChange_eager( workInProgress: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, renderLanes: Lanes, ): void { // Only used by eager implementation @@ -341,7 +346,7 @@ function propagateContextChange_eager( } } -function propagateContextChanges( +function propagateContextChanges( workInProgress: Fiber, contexts: Array, renderLanes: Lanes, @@ -370,7 +375,7 @@ function propagateContextChanges( const dependency = dep; const consumer = fiber; findContext: for (let i = 0; i < contexts.length; i++) { - const context: ReactContext = contexts[i]; + const context: ReactContext | ReactServerContext = contexts[i]; // Check if the context matches. // TODO: Compare selected values to bail out early. if (dependency.context === context) { @@ -537,7 +542,8 @@ function propagateParentContextChanges( const oldProps = currentParent.memoizedProps; if (oldProps !== null) { const providerType: ReactProviderType = parent.type; - const context: ReactContext = providerType._context; + const context: ReactContext | ReactServerContext = + providerType._context; const newProps = parent.pendingProps; const newValue = newProps.value; @@ -640,7 +646,9 @@ export function prepareToReadContext( } } -export function readContext(context: ReactContext): T { +export function readContext( + context: ReactContext | ReactServerContext, +): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 93fe3bc8395..0adc1d03c6b 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; +import type { + ReactContext, + ReactServerContext, + ReactProviderType, +} from 'shared/ReactTypes'; import type { Fiber, ContextDependency, @@ -83,9 +87,9 @@ export function exitDisallowedContextReadInDEV(): void { } } -export function pushProvider( +export function pushProvider( providerFiber: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, nextValue: T, ): void { if (isPrimaryRenderer) { @@ -125,16 +129,16 @@ export function pushProvider( } } -export function popProvider( - context: ReactContext, +export function popProvider( + context: ReactContext | ReactServerContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = currentValue; + context._currentValue = (currentValue: any); } else { - context._currentValue2 = currentValue; + context._currentValue2 = (currentValue: any); } } @@ -180,9 +184,9 @@ export function scheduleContextWorkOnParentPath( } } -export function propagateContextChange( +export function propagateContextChange( workInProgress: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, renderLanes: Lanes, ): void { if (enableLazyContextPropagation) { @@ -201,9 +205,9 @@ export function propagateContextChange( } } -function propagateContextChange_eager( +function propagateContextChange_eager( workInProgress: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, renderLanes: Lanes, ): void { // Only used by eager implementation @@ -341,7 +345,7 @@ function propagateContextChange_eager( } } -function propagateContextChanges( +function propagateContextChanges( workInProgress: Fiber, contexts: Array, renderLanes: Lanes, @@ -370,7 +374,7 @@ function propagateContextChanges( const dependency = dep; const consumer = fiber; findContext: for (let i = 0; i < contexts.length; i++) { - const context: ReactContext = contexts[i]; + const context: ReactContext | ReactServerContext = contexts[i]; // Check if the context matches. // TODO: Compare selected values to bail out early. if (dependency.context === context) { @@ -537,7 +541,8 @@ function propagateParentContextChanges( const oldProps = currentParent.memoizedProps; if (oldProps !== null) { const providerType: ReactProviderType = parent.type; - const context: ReactContext = providerType._context; + const context: ReactContext | ReactServerContext = + providerType._context; const newProps = parent.pendingProps; const newValue = newProps.value; @@ -640,7 +645,9 @@ export function prepareToReadContext( } } -export function readContext(context: ReactContext): T { +export function readContext( + context: ReactContext | ReactServerContext, +): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. diff --git a/packages/react-reconciler/src/ReactFiberScope.new.js b/packages/react-reconciler/src/ReactFiberScope.new.js index ccc589a9dfe..491e82d89d3 100644 --- a/packages/react-reconciler/src/ReactFiberScope.new.js +++ b/packages/react-reconciler/src/ReactFiberScope.new.js @@ -12,6 +12,7 @@ import type { ReactScopeInstance, ReactContext, ReactScopeQuery, + ReactServerContext, } from 'shared/ReactTypes'; import { @@ -108,9 +109,9 @@ function collectFirstScopedNodeFromChildren( return null; } -function collectNearestContextValues( +function collectNearestContextValues( node: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, childContextValues: Array, ): void { if (node.tag === ContextProvider && node.type._context === context) { @@ -128,9 +129,9 @@ function collectNearestContextValues( } } -function collectNearestChildContextValues( +function collectNearestChildContextValues( startingChild: Fiber | null, - context: ReactContext, + context: ReactContext | ReactServerContext, childContextValues: Array, ): void { let child = startingChild; @@ -176,7 +177,9 @@ function containsNode(node: Object): boolean { return false; } -function getChildContextValues(context: ReactContext): Array { +function getChildContextValues( + context: ReactContext | ReactServerContext, +): Array { const currentFiber = getInstanceFromScope(this); if (currentFiber === null) { return []; diff --git a/packages/react-reconciler/src/ReactFiberScope.old.js b/packages/react-reconciler/src/ReactFiberScope.old.js index ccc589a9dfe..491e82d89d3 100644 --- a/packages/react-reconciler/src/ReactFiberScope.old.js +++ b/packages/react-reconciler/src/ReactFiberScope.old.js @@ -12,6 +12,7 @@ import type { ReactScopeInstance, ReactContext, ReactScopeQuery, + ReactServerContext, } from 'shared/ReactTypes'; import { @@ -108,9 +109,9 @@ function collectFirstScopedNodeFromChildren( return null; } -function collectNearestContextValues( +function collectNearestContextValues( node: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, childContextValues: Array, ): void { if (node.tag === ContextProvider && node.type._context === context) { @@ -128,9 +129,9 @@ function collectNearestContextValues( } } -function collectNearestChildContextValues( +function collectNearestChildContextValues( startingChild: Fiber | null, - context: ReactContext, + context: ReactContext | ReactServerContext, childContextValues: Array, ): void { let child = startingChild; @@ -176,7 +177,9 @@ function containsNode(node: Object): boolean { return false; } -function getChildContextValues(context: ReactContext): Array { +function getChildContextValues( + context: ReactContext | ReactServerContext, +): Array { const currentFiber = getInstanceFromScope(this); if (currentFiber === null) { return []; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index f3efac3e74d..eca412d017b 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -1418,7 +1418,6 @@ function handleError(root, thrownValue): void { ); } } - throwException( root, erroredWork.return, diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index bbfe70c26cf..50ad16380c1 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -11,10 +11,13 @@ import type {Source} from 'shared/ReactElementType'; import type { RefObject, ReactContext, + ReactServerContext, + ServerContextJSONValue, MutableSourceSubscribeFn, MutableSourceGetSnapshotFn, MutableSourceVersion, MutableSource, + Wakeable, } from 'shared/ReactTypes'; import type {SuspenseInstance} from './ReactFiberHostConfig'; import type {WorkTag} from './ReactWorkTags'; @@ -23,7 +26,6 @@ import type {Flags} from './ReactFiberFlags'; import type {Lane, Lanes, LaneMap} from './ReactFiberLane.old'; import type {RootTag} from './ReactRootTags'; import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig'; -import type {Wakeable} from 'shared/ReactTypes'; import type {Cache} from './ReactFiberCacheComponent.old'; // Unwind Circular: moved from ReactFiberHooks.old @@ -44,10 +46,11 @@ export type HookType = | 'useMutableSource' | 'useSyncExternalStore' | 'useId' - | 'useCacheRefresh'; + | 'useCacheRefresh' + | 'useServerContext'; -export type ContextDependency = { - context: ReactContext, +export type ContextDependency = { + context: ReactContext | ReactServerContext, next: ContextDependency | null, memoizedValue: T, ... @@ -284,7 +287,7 @@ type Dispatch = A => void; export type Dispatcher = {| getCacheSignal?: () => AbortSignal, getCacheForType?: (resourceType: () => T) => T, - readContext(context: ReactContext): T, + readContext(context: ReactContext | ReactServerContext): T, useState(initialState: (() => S) | S): [S, Dispatch>], useReducer( reducer: (S, A) => S, @@ -320,6 +323,9 @@ export type Dispatcher = {| getSnapshot: MutableSourceGetSnapshotFn, subscribe: MutableSourceSubscribeFn, ): Snapshot, + useServerContext( + context: ReactServerContext, + ): T, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js index 85eace536ac..618226a1dc4 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js @@ -75,45 +75,12 @@ export function processErrorChunk( ]; } -function convertModelToJSON( - request: Request, - parent: {+[key: string]: ReactModel} | $ReadOnlyArray, - key: string, - model: ReactModel, -): JSONValue { - const json = resolveModelToJSON(request, parent, key, model); - if (typeof json === 'object' && json !== null) { - if (isArray(json)) { - const jsonArray: Array = []; - for (let i = 0; i < json.length; i++) { - jsonArray[i] = convertModelToJSON(request, json, '' + i, json[i]); - } - return jsonArray; - } else { - const jsonObj: {[key: string]: JSONValue} = {}; - for (const nextKey in json) { - if (hasOwnProperty.call(json, nextKey)) { - jsonObj[nextKey] = convertModelToJSON( - request, - json, - nextKey, - json[nextKey], - ); - } - } - return jsonObj; - } - } - return json; -} - export function processModelChunk( request: Request, id: number, - model: ReactModel, + jsonValue: JSONValue, ): Chunk { - const json = convertModelToJSON(request, {}, '', model); - return ['J', id, json]; + return ['J', id, jsonValue]; } export function processModuleChunk( diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js index 9d632dfb071..c7286509b51 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js @@ -7,7 +7,10 @@ * @flow */ -import type {ReactModel} from 'react-server/src/ReactFlightServer'; +import type { + ReactModel, + RequestOptions, +} from 'react-server/src/ReactFlightServer'; import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig'; import { @@ -16,20 +19,12 @@ import { startFlowing, } from 'react-server/src/ReactFlightServer'; -type Options = { - onError?: (error: mixed) => void, -}; - function renderToReadableStream( model: ReactModel, webpackMap: BundlerConfig, - options?: Options, + options?: RequestOptions, ): ReadableStream { - const request = createRequest( - model, - webpackMap, - options ? options.onError : undefined, - ); + const request = createRequest(model, webpackMap, options); const stream = new ReadableStream({ start(controller) { startWork(request); diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js index 5f992d4b03e..92abb8afae1 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js @@ -7,7 +7,10 @@ * @flow */ -import type {ReactModel} from 'react-server/src/ReactFlightServer'; +import type { + ReactModel, + RequestOptions, +} from 'react-server/src/ReactFlightServer'; import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig'; import type {Writable} from 'stream'; @@ -21,10 +24,6 @@ function createDrainHandler(destination, request) { return () => startFlowing(request, destination); } -type Options = { - onError?: (error: mixed) => void, -}; - type Controls = {| pipe(destination: T): T, |}; @@ -32,13 +31,9 @@ type Controls = {| function renderToPipeableStream( model: ReactModel, webpackMap: BundlerConfig, - options?: Options, + options?: RequestOptions, ): Controls { - const request = createRequest( - model, - webpackMap, - options ? options.onError : undefined, - ); + const request = createRequest(model, webpackMap, options); let hasStartedFlowing = false; startWork(request); return { diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js index 0624e693cfe..f57017154c3 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js @@ -107,10 +107,9 @@ function convertModelToJSON( export function processModelChunk( request: Request, id: number, - model: ReactModel, + jsonValue: JSONValue, ): Chunk { - const json = convertModelToJSON(request, {}, '', model); - return ['J', id, json]; + return ['J', id, jsonValue]; } export function processModuleChunk( diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index 5997f1b02b9..1b04474a602 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -14,6 +14,8 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, + ServerContextJSONValue, } from 'shared/ReactTypes'; import type {ResponseState} from './ReactServerFormatConfig'; @@ -242,7 +244,9 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext(context: ReactContext): T { +function readContext( + context: ReactContext | ReactServerContext, +): T { if (__DEV__) { if (isInHookUserCodeInDev) { console.error( @@ -264,6 +268,16 @@ function useContext(context: ReactContext): T { return readContextImpl(context); } +function useServerContext( + context: ReactServerContext, +): T { + if (__DEV__) { + currentHookNameInDev = 'useServerContext'; + } + resolveCurrentlyRenderingComponent(); + return readContextImpl(context); +} + function basicStateReducer(state: S, action: BasicStateAction): S { // $FlowFixMe: Flow doesn't like mixed types return typeof action === 'function' ? action(state) : action; @@ -538,6 +552,7 @@ function noop(): void {} export const Dispatcher: DispatcherType = { readContext, useContext, + useServerContext, useMemo, useReducer, useRef, diff --git a/packages/react-server/src/ReactFizzNewContext.js b/packages/react-server/src/ReactFizzNewContext.js index 0eaa07f839a..01df3d84494 100644 --- a/packages/react-server/src/ReactFizzNewContext.js +++ b/packages/react-server/src/ReactFizzNewContext.js @@ -7,7 +7,7 @@ * @flow */ -import type {ReactContext} from 'shared/ReactTypes'; +import type {ReactContext, ReactServerContext} from 'shared/ReactTypes'; import {isPrimaryRenderer} from './ReactServerFormatConfig'; @@ -19,10 +19,10 @@ if (__DEV__) { // Used to store the parent path of all context overrides in a shared linked list. // Forming a reverse tree. -type ContextNode = { +type ContextNode = { parent: null | ContextNode, depth: number, // Short hand to compute the depth of the tree at this node. - context: ReactContext, + context: ReactContext | ReactServerContext, parentValue: T, value: T, }; @@ -177,8 +177,8 @@ export function switchContext(newSnapshot: ContextSnapshot): void { } } -export function pushProvider( - context: ReactContext, +export function pushProvider( + context: ReactContext | ReactServerContext, nextValue: T, ): ContextSnapshot { let prevValue; @@ -227,7 +227,9 @@ export function pushProvider( return newNode; } -export function popProvider(context: ReactContext): ContextSnapshot { +export function popProvider( + context: ReactContext | ReactServerContext, +): ContextSnapshot { const prevSnapshot = currentActiveSnapshot; if (prevSnapshot === null) { @@ -281,7 +283,9 @@ export function getActiveContext(): ContextSnapshot { return currentActiveSnapshot; } -export function readContext(context: ReactContext): T { +export function readContext( + context: ReactContext | ReactServerContext, +): T { const value = isPrimaryRenderer ? context._currentValue : context._currentValue2; diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js new file mode 100644 index 00000000000..36b37eb4a5d --- /dev/null +++ b/packages/react-server/src/ReactFlightHooks.js @@ -0,0 +1,113 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes'; +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; +import {readContext as readContextImpl} from './ReactFlightNewContext'; + +function readContext( + context: ReactServerContext, +): T { + if (__DEV__) { + if (currentCache === null) { + console.error( + 'Context can only be read while React is rendering. ' + + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + + 'In function components, you can read it directly in the function body, but not ' + + 'inside Hooks like useReducer() or useMemo().', + ); + } + } + return readContextImpl(context); +} + +export const Dispatcher: DispatcherType = { + useMemo(nextCreate: () => T): T { + return nextCreate(); + }, + useCallback(callback: T): T { + return callback; + }, + useDebugValue(): void {}, + useDeferredValue: (unsupportedHook: any), + useTransition: (unsupportedHook: any), + getCacheForType(resourceType: () => T): T { + if (!currentCache) { + throw new Error('Reading the cache is only supported while rendering.'); + } + + let entry: T | void = (currentCache.get(resourceType): any); + if (entry === undefined) { + entry = resourceType(); + // TODO: Warn if undefined? + currentCache.set(resourceType, entry); + } + return entry; + }, + readContext, + useContext: (unsupportedHook: any), + useReducer: (unsupportedHook: any), + useRef: (unsupportedHook: any), + useState: (unsupportedHook: any), + useInsertionEffect: (unsupportedHook: any), + useLayoutEffect: (unsupportedHook: any), + useImperativeHandle: (unsupportedHook: any), + useEffect: (unsupportedHook: any), + useId: (unsupportedHook: any), + useMutableSource: (unsupportedHook: any), + useServerContext: function useServerContext( + context: ReactServerContext, + ): T { + if (!currentCache) { + throw new Error('useServerContext is only supported while rendering.'); + } + return readContextImpl(context); + }, + useSyncExternalStore: (unsupportedHook: any), + useCacheRefresh(): (?() => T, ?T) => void { + return unsupportedRefresh; + }, +}; + +function unsupportedHook(): void { + throw new Error('This Hook is not supported in Server Components.'); +} + +function unsupportedRefresh(): void { + if (!currentCache) { + throw new Error( + 'Refreshing the cache is not supported in Server Components.', + ); + } +} + +let currentCache: Map | null = null; + +export function setCurrentCache(cache: Map | null) { + currentCache = cache; + return currentCache; +} + +export function getCurrentCache() { + return currentCache; +} + +type ServerContextCache = {[name: string]: ReactServerContext} | null; +let currentServerContexts: ServerContextCache = null; + +export function setCurrentServerContexts(contexts: ServerContextCache) { + currentServerContexts = contexts; +} + +export function getCurrentServerContexts(): ServerContextCache { + return currentServerContexts; +} diff --git a/packages/react-server/src/ReactFlightNewContext.js b/packages/react-server/src/ReactFlightNewContext.js new file mode 100644 index 00000000000..b3c52c71157 --- /dev/null +++ b/packages/react-server/src/ReactFlightNewContext.js @@ -0,0 +1,246 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; + +let rendererSigil; +if (__DEV__) { + // Use this to detect multiple renderers using the same context + rendererSigil = {}; +} + +// Used to store the parent path of all context overrides in a shared linked list. +// Forming a reverse tree. +type ContextNode = { + parent: null | ContextNode, + depth: number, // Short hand to compute the depth of the tree at this node. + context: ReactServerContext, + parentValue: T, + value: T, +}; + +// The structure of a context snapshot is an implementation of this file. +// Currently, it's implemented as tracking the current active node. +export opaque type ContextSnapshot = null | ContextNode; + +export const rootContextSnapshot: ContextSnapshot = null; + +// We assume that this runtime owns the "current" field on all ReactContext instances. +// This global (actually thread local) state represents what state all those "current", +// fields are currently in. +let currentActiveSnapshot: ContextSnapshot = null; + +function popNode(prev: ContextNode): void { + prev.context._currentValue = prev.parentValue; +} + +function pushNode(next: ContextNode): void { + next.context._currentValue = next.value; +} + +function popToNearestCommonAncestor( + prev: ContextNode, + next: ContextNode, +): void { + if (prev === next) { + // We've found a shared ancestor. We don't need to pop nor reapply this one or anything above. + } else { + popNode(prev); + const parentPrev = prev.parent; + const parentNext = next.parent; + if (parentPrev === null) { + if (parentNext !== null) { + throw new Error( + 'The stacks must reach the root at the same time. This is a bug in React.', + ); + } + } else { + if (parentNext === null) { + throw new Error( + 'The stacks must reach the root at the same time. This is a bug in React.', + ); + } + + popToNearestCommonAncestor(parentPrev, parentNext); + // On the way back, we push the new ones that weren't common. + pushNode(next); + } + } +} + +function popAllPrevious(prev: ContextNode): void { + popNode(prev); + const parentPrev = prev.parent; + if (parentPrev !== null) { + popAllPrevious(parentPrev); + } +} + +function pushAllNext(next: ContextNode): void { + const parentNext = next.parent; + if (parentNext !== null) { + pushAllNext(parentNext); + } + pushNode(next); +} + +function popPreviousToCommonLevel( + prev: ContextNode, + next: ContextNode, +): void { + popNode(prev); + const parentPrev = prev.parent; + + if (parentPrev === null) { + throw new Error( + 'The depth must equal at least at zero before reaching the root. This is a bug in React.', + ); + } + + if (parentPrev.depth === next.depth) { + // We found the same level. Now we just need to find a shared ancestor. + popToNearestCommonAncestor(parentPrev, next); + } else { + // We must still be deeper. + popPreviousToCommonLevel(parentPrev, next); + } +} + +function popNextToCommonLevel( + prev: ContextNode, + next: ContextNode, +): void { + const parentNext = next.parent; + + if (parentNext === null) { + throw new Error( + 'The depth must equal at least at zero before reaching the root. This is a bug in React.', + ); + } + + if (prev.depth === parentNext.depth) { + // We found the same level. Now we just need to find a shared ancestor. + popToNearestCommonAncestor(prev, parentNext); + } else { + // We must still be deeper. + popNextToCommonLevel(prev, parentNext); + } + pushNode(next); +} + +// Perform context switching to the new snapshot. +// To make it cheap to read many contexts, while not suspending, we make the switch eagerly by +// updating all the context's current values. That way reads, always just read the current value. +// At the cost of updating contexts even if they're never read by this subtree. +export function switchContext(newSnapshot: ContextSnapshot): void { + // The basic algorithm we need to do is to pop back any contexts that are no longer on the stack. + // We also need to update any new contexts that are now on the stack with the deepest value. + // The easiest way to update new contexts is to just reapply them in reverse order from the + // perspective of the backpointers. To avoid allocating a lot when switching, we use the stack + // for that. Therefore this algorithm is recursive. + // 1) First we pop which ever snapshot tree was deepest. Popping old contexts as we go. + // 2) Then we find the nearest common ancestor from there. Popping old contexts as we go. + // 3) Then we reapply new contexts on the way back up the stack. + const prev = currentActiveSnapshot; + const next = newSnapshot; + if (prev !== next) { + if (prev === null) { + // $FlowFixMe: This has to be non-null since it's not equal to prev. + pushAllNext(next); + } else if (next === null) { + popAllPrevious(prev); + } else if (prev.depth === next.depth) { + popToNearestCommonAncestor(prev, next); + } else if (prev.depth > next.depth) { + popPreviousToCommonLevel(prev, next); + } else { + popNextToCommonLevel(prev, next); + } + currentActiveSnapshot = next; + } +} + +export function pushProvider( + context: ReactServerContext, + nextValue: T, +): ContextSnapshot { + const prevValue = context._currentValue; + context._currentValue = nextValue; + if (__DEV__) { + if ( + context._currentRenderer !== undefined && + context._currentRenderer !== null && + context._currentRenderer !== rendererSigil + ) { + console.error( + 'Detected multiple renderers concurrently rendering the ' + + 'same context provider. This is currently unsupported.', + ); + } + context._currentRenderer = rendererSigil; + } + const prevNode = currentActiveSnapshot; + const newNode: ContextNode = { + parent: prevNode, + depth: prevNode === null ? 0 : prevNode.depth + 1, + context: context, + parentValue: prevValue, + value: nextValue, + }; + currentActiveSnapshot = newNode; + return newNode; +} + +export function popProvider( + context: ReactServerContext, +): ContextSnapshot { + const prevSnapshot = currentActiveSnapshot; + + if (prevSnapshot === null) { + throw new Error( + 'Tried to pop a Context at the root of the app. This is a bug in React.', + ); + } + + if (__DEV__) { + if (prevSnapshot.context !== context) { + console.error( + 'The parent context is not the expected context. This is probably a bug in React.', + ); + } + } + prevSnapshot.context._currentValue = prevSnapshot.parentValue; + if (__DEV__) { + if ( + context._currentRenderer !== undefined && + context._currentRenderer !== null && + context._currentRenderer !== rendererSigil + ) { + console.error( + 'Detected multiple renderers concurrently rendering the ' + + 'same context provider. This is currently unsupported.', + ); + } + context._currentRenderer = rendererSigil; + } + return (currentActiveSnapshot = prevSnapshot.parent); +} + +export function getActiveContext(): ContextSnapshot { + return currentActiveSnapshot; +} + +export function readContext( + context: ReactServerContext, +): T { + return context._currentValue; +} diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 052fa730fd9..649c4f7f35a 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -7,7 +7,6 @@ * @flow */ -import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes'; import type { Destination, Chunk, @@ -16,6 +15,11 @@ import type { ModuleReference, ModuleKey, } from './ReactFlightServerConfig'; +import type {ContextSnapshot} from './ReactFlightNewContext'; +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; import { scheduleWork, @@ -34,12 +38,28 @@ import { isModuleReference, } from './ReactFlightServerConfig'; +import {createServerContext} from 'react/src/ReactServerContext'; +import { + Dispatcher, + getCurrentCache, + setCurrentCache, + setCurrentServerContexts, +} from './ReactFlightHooks'; +import { + pushProvider, + popProvider, + switchContext, + getActiveContext, + rootContextSnapshot, +} from './ReactFlightNewContext'; + import { REACT_ELEMENT_TYPE, REACT_FORWARD_REF_TYPE, REACT_FRAGMENT_TYPE, REACT_LAZY_TYPE, REACT_MEMO_TYPE, + REACT_PROVIDER_TYPE, } from 'shared/ReactSymbols'; import ReactSharedInternals from 'shared/ReactSharedInternals'; @@ -68,6 +88,7 @@ type Segment = { id: number, model: ReactModel, ping: () => void, + context: ContextSnapshot, }; export type Request = { @@ -88,6 +109,11 @@ export type Request = { toJSON: (key: string, value: ReactModel) => ReactJSONValue, }; +export type RequestOptions = { + onError?: (error: mixed) => void, + context?: Array<{name: string, value: ServerContextJSONValue}>, +}; + const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; function defaultErrorHandler(error: mixed) { @@ -102,9 +128,10 @@ const CLOSED = 2; export function createRequest( model: ReactModel, bundlerConfig: BundlerConfig, - onError: void | ((error: mixed) => void), + options?: RequestOptions, ): Request { const pingedSegments = []; + const onError = options?.onError; const request = { status: OPEN, fatalError: null, @@ -125,11 +152,18 @@ export function createRequest( }, }; request.pendingChunks++; - const rootSegment = createSegment(request, model); + const context = createRootContext(options?.context); + const rootSegment = createSegment(request, model, context); pingedSegments.push(rootSegment); return request; } +function createRootContext( + reqContext?: Array<{name: string, value: ServerContextJSONValue}>, +) { + return importServerContexts(reqContext); +} + function attemptResolveElement( type: any, key: null | React$Key, @@ -174,6 +208,9 @@ function attemptResolveElement( case REACT_MEMO_TYPE: { return attemptResolveElement(type.type, key, ref, props); } + case REACT_PROVIDER_TYPE: { + return [REACT_PROVIDER_TYPE, type._context.displayName, key, props]; + } } } throw new Error( @@ -189,11 +226,16 @@ function pingSegment(request: Request, segment: Segment): void { } } -function createSegment(request: Request, model: ReactModel): Segment { +function createSegment( + request: Request, + model: ReactModel, + context: ContextSnapshot, +): Segment { const id = request.nextChunkId++; const segment = { id, model, + context, ping: () => pingSegment(request, segment), }; return segment; @@ -207,6 +249,10 @@ function serializeByRefID(id: number): string { return '@' + id.toString(16); } +function serializeByContextID(id: number): string { + return '!'; +} + function escapeStringValue(value: string): string { if (value[0] === '$' || value[0] === '@') { // We need to escape $ or @ prefixed strings since we use those to encode @@ -221,7 +267,6 @@ function isObjectPrototype(object): boolean { if (!object) { return false; } - // $FlowFixMe const ObjectPrototype = Object.prototype; if (object === ObjectPrototype) { return true; @@ -311,7 +356,6 @@ function describeObjectForErrorMessage( ): string { if (isArray(objectOrArray)) { let str = '['; - // $FlowFixMe: Should be refined by now. const array: $ReadOnlyArray = objectOrArray; for (let i = 0; i < array.length; i++) { if (i > 0) { @@ -336,7 +380,6 @@ function describeObjectForErrorMessage( return str; } else { let str = '{'; - // $FlowFixMe: Should be refined by now. const object: {+[key: string | number]: ReactModel} = objectOrArray; const names = Object.keys(object); for (let i = 0; i < names.length; i++) { @@ -390,6 +433,8 @@ export function resolveModelToJSON( switch (value) { case REACT_ELEMENT_TYPE: return '$'; + case REACT_PROVIDER_TYPE: + return '!'; case REACT_LAZY_TYPE: throw new Error( 'React Lazy Components are not yet supported on the server.', @@ -412,11 +457,12 @@ export function resolveModelToJSON( element.ref, element.props, ); + 2; } catch (x) { if (typeof x === 'object' && x !== null && typeof x.then === 'function') { // Something suspended, we'll need to create a new segment and resolve it later. request.pendingChunks++; - const newSegment = createSegment(request, value); + const newSegment = createSegment(request, value, getActiveContext()); const ping = newSegment.ping; x.then(ping, ping); return serializeByRefID(newSegment.id); @@ -658,6 +704,7 @@ function emitSymbolChunk(request: Request, id: number, name: string): void { } function retrySegment(request: Request, segment: Segment): void { + switchContext(segment.context); try { let value = segment.model; while ( @@ -677,8 +724,13 @@ function retrySegment(request: Request, segment: Segment): void { element.ref, element.props, ); + 1; } - const processedChunk = processModelChunk(request, segment.id, value); + const processedChunk = processModelChunk( + request, + segment.id, + convertModelToJSON(request, {'': value}, '', value), + ); request.completedJSONChunks.push(processedChunk); } catch (x) { if (typeof x === 'object' && x !== null && typeof x.then === 'function') { @@ -696,9 +748,9 @@ function retrySegment(request: Request, segment: Segment): void { function performWork(request: Request): void { const prevDispatcher = ReactCurrentDispatcher.current; - const prevCache = currentCache; + const prevCache = getCurrentCache(); ReactCurrentDispatcher.current = Dispatcher; - currentCache = request.cache; + setCurrentCache(request.cache); try { const pingedSegments = request.pingedSegments; @@ -715,7 +767,7 @@ function performWork(request: Request): void { fatalError(request, error); } finally { ReactCurrentDispatcher.current = prevDispatcher; - currentCache = prevCache; + setCurrentCache(prevCache); } } @@ -799,56 +851,76 @@ export function startFlowing(request: Request, destination: Destination): void { } } -function unsupportedHook(): void { - throw new Error('This Hook is not supported in Server Components.'); -} - -function unsupportedRefresh(): void { - if (!currentCache) { - throw new Error( - 'Refreshing the cache is not supported in Server Components.', - ); +function importServerContexts( + contexts: + | Array<{name: string, value: ServerContextJSONValue}> + | typeof undefined, +) { + const registry: {[name: string]: ReactServerContext} = {}; + if (contexts) { + for (let i = 0; i < contexts.length; i++) { + const {name, value} = contexts[i]; + const context = createServerContext(name, null); + pushProvider(context, value); + registry[name] = context; + } } + setCurrentServerContexts(registry); + return getActiveContext(); } -let currentCache: Map | null = null; - -const Dispatcher: DispatcherType = { - useMemo(nextCreate: () => T): T { - return nextCreate(); - }, - useCallback(callback: T): T { - return callback; - }, - useDebugValue(): void {}, - useDeferredValue: (unsupportedHook: any), - useTransition: (unsupportedHook: any), - getCacheForType(resourceType: () => T): T { - if (!currentCache) { - throw new Error('Reading the cache is only supported while rendering.'); +function convertModelToJSON( + request: Request, + parent: {+[key: string]: ReactModel} | $ReadOnlyArray, + key: string, + model: ReactModel, +): JSONValue { + const isReactElement = + typeof model === 'object' && + model !== null && + (model: any).$$typeof === REACT_ELEMENT_TYPE; + + let context; + if (isReactElement) { + const element: React$Element = (model: any); + if (element.type.$$typeof === REACT_PROVIDER_TYPE) { + context = element.type._context; + pushProvider(context, element.props.value); } + } - let entry: T | void = (currentCache.get(resourceType): any); - if (entry === undefined) { - entry = resourceType(); - // TODO: Warn if undefined? - currentCache.set(resourceType, entry); + const json = resolveModelToJSON(request, parent, key, model); + + if (typeof json === 'object' && json !== null) { + if (isArray(json)) { + const jsonArray: Array = []; + for (let i = 0; i < json.length; i++) { + jsonArray[i] = convertModelToJSON(request, json, '' + i, json[i]); + } + if (context) { + popProvider(context); + } + return jsonArray; + } else { + const jsonObj: {[key: string]: JSONValue} = {}; + for (const nextKey in json) { + if (hasOwnProperty.call(json, nextKey)) { + jsonObj[nextKey] = convertModelToJSON( + request, + json, + nextKey, + json[nextKey], + ); + } + } + if (context) { + popProvider(context); + } + return jsonObj; } - return entry; - }, - readContext: (unsupportedHook: any), - useContext: (unsupportedHook: any), - useReducer: (unsupportedHook: any), - useRef: (unsupportedHook: any), - useState: (unsupportedHook: any), - useInsertionEffect: (unsupportedHook: any), - useLayoutEffect: (unsupportedHook: any), - useImperativeHandle: (unsupportedHook: any), - useEffect: (unsupportedHook: any), - useId: (unsupportedHook: any), - useMutableSource: (unsupportedHook: any), - useSyncExternalStore: (unsupportedHook: any), - useCacheRefresh(): (?() => T, ?T) => void { - return unsupportedRefresh; - }, -}; + } + if (context) { + popProvider(context); + } + return json; +} diff --git a/packages/react-server/src/ReactFlightServerConfigStream.js b/packages/react-server/src/ReactFlightServerConfigStream.js index 4ff841bfc11..13cbcf76141 100644 --- a/packages/react-server/src/ReactFlightServerConfigStream.js +++ b/packages/react-server/src/ReactFlightServerConfigStream.js @@ -92,10 +92,9 @@ export function processErrorChunk( export function processModelChunk( request: Request, id: number, - model: ReactModel, + jsonValue: JSONValue, ): Chunk { - const json = stringify(model, request.toJSON); - const row = serializeRowHeader('J', id) + json + '\n'; + const row = serializeRowHeader('J', id) + JSON.stringify(jsonValue) + '\n'; return stringToChunk(row); } diff --git a/packages/react/index.classic.fb.js b/packages/react/index.classic.fb.js index 33c770f00fe..122f679fcfd 100644 --- a/packages/react/index.classic.fb.js +++ b/packages/react/index.classic.fb.js @@ -26,6 +26,7 @@ export { createMutableSource, createMutableSource as unstable_createMutableSource, createRef, + createServerContext, forwardRef, isValidElement, lazy, @@ -53,10 +54,11 @@ export { useMemo, useMutableSource, useMutableSource as unstable_useMutableSource, - useSyncExternalStore, useReducer, useRef, + useServerContext, useState, + useSyncExternalStore, useTransition, useTransition as unstable_useTransition, // TODO: Remove once call sights updated to useTransition version, diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js index d4dc33a4db0..1ee0c52731f 100644 --- a/packages/react/index.experimental.js +++ b/packages/react/index.experimental.js @@ -24,6 +24,7 @@ export { createFactory, createMutableSource as unstable_createMutableSource, createRef, + createServerContext, forwardRef, isValidElement, lazy, @@ -46,10 +47,11 @@ export { useInsertionEffect, useLayoutEffect, useMemo, - useSyncExternalStore, useReducer, useRef, + useServerContext, useState, + useSyncExternalStore, useTransition, version, } from './src/React'; diff --git a/packages/react/index.js b/packages/react/index.js index 6a249ba432c..06cf6367f55 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -48,6 +48,7 @@ export { createFactory, createMutableSource, createRef, + createServerContext, forwardRef, isValidElement, lazy, @@ -72,6 +73,7 @@ export { useLayoutEffect, useMemo, useMutableSource, + useServerContext, useSyncExternalStore, useReducer, useRef, diff --git a/packages/react/index.modern.fb.js b/packages/react/index.modern.fb.js index 5b0d75e2146..231f094922f 100644 --- a/packages/react/index.modern.fb.js +++ b/packages/react/index.modern.fb.js @@ -25,6 +25,7 @@ export { createMutableSource, createMutableSource as unstable_createMutableSource, createRef, + createServerContext, forwardRef, isValidElement, lazy, @@ -52,10 +53,11 @@ export { useMemo, useMutableSource, useMutableSource as unstable_useMutableSource, - useSyncExternalStore, useReducer, useRef, + useServerContext, useState, + useSyncExternalStore, useTransition, useTransition as unstable_useTransition, // TODO: Remove once call sights updated to useTransition version, diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js index b0f6f409bb0..e91878c4d8a 100644 --- a/packages/react/index.stable.js +++ b/packages/react/index.stable.js @@ -23,6 +23,7 @@ export { createFactory, createMutableSource as unstable_createMutableSource, createRef, + createServerContext, forwardRef, isValidElement, lazy, @@ -38,10 +39,11 @@ export { useInsertionEffect, useLayoutEffect, useMemo, - useSyncExternalStore, useReducer, useRef, + useServerContext, useState, + useSyncExternalStore, useTransition, version, } from './src/React'; diff --git a/packages/react/src/React.js b/packages/react/src/React.js index 891269ee326..43f5673ac96 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -31,6 +31,7 @@ import { isValidElement, } from './ReactElement'; import {createContext} from './ReactContext'; +import {createServerContext} from './ReactServerContext'; import {lazy} from './ReactLazy'; import {forwardRef} from './ReactForwardRef'; import {memo} from './ReactMemo'; @@ -49,6 +50,7 @@ import { useSyncExternalStore, useReducer, useRef, + useServerContext, useState, useTransition, useDeferredValue, @@ -85,6 +87,7 @@ export { Component, PureComponent, createContext, + createServerContext, forwardRef, lazy, memo, @@ -97,6 +100,7 @@ export { useLayoutEffect, useMemo, useMutableSource, + useServerContext, useSyncExternalStore, useReducer, useRef, diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 12a5350083f..2c7bc094a1a 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -13,6 +13,8 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, + ServerContextJSONValue, } from 'shared/ReactTypes'; import ReactCurrentDispatcher from './ReactCurrentDispatcher'; @@ -182,6 +184,14 @@ export function useMutableSource( return dispatcher.useMutableSource(source, getSnapshot, subscribe); } +export function useServerContext( + Context: ReactServerContext, +): T { + // TODO: Warn if regular context is passed in + const dispatcher = resolveDispatcher(); + return dispatcher.useServerContext(Context); +} + export function useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js new file mode 100644 index 00000000000..8d4d93ea457 --- /dev/null +++ b/packages/react/src/ReactServerContext.js @@ -0,0 +1,63 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; + +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; + +const globalRegistry: { + [globalName: string]: ReactServerContext, +} = {}; + +export function createServerContext( + globalName: string, + defaultValue: T, +): ReactServerContext { + if (globalRegistry[globalName]) { + throw new Error('ServerContext in that name already exists'); + } + const context: ReactServerContext = { + $$typeof: REACT_CONTEXT_TYPE, + // As a workaround to support multiple concurrent renderers, we categorize + // some renderers as primary and others as secondary. We only expect + // there to be two concurrent renderers at most: React Native (primary) and + // Fabric (secondary); React DOM (primary) and React ART (secondary). + // Secondary renderers store their context values on separate fields. + _currentValue: defaultValue, + _currentValue2: defaultValue, + // Used to track how many concurrent renderers this context currently + // supports within in a single renderer. Such as parallel server rendering. + _threadCount: 0, + // These are circular + Provider: (null: any), + displayName: globalName, + }; + + context.Provider = { + $$typeof: REACT_PROVIDER_TYPE, + _context: context, + }; + + if (__DEV__) { + context._currentRenderer = null; + context._currentRenderer2 = null; + } + globalRegistry[globalName] = context; + return context; +} + +export function getOrCreateContextByName(name: string) { + if (!globalRegistry[name]) { + globalRegistry[name] = createServerContext(name, null); + } + return globalRegistry[name]; +} diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 43f42bddb91..9bfeca29dcb 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -38,13 +38,19 @@ export type ReactProvider = { export type ReactProviderType = { $$typeof: Symbol | number, - _context: ReactContext, + _context: ReactContext | ReactServerContext, + ... +}; + +export type ReactServerProviderType = { + $$typeof: Symbol | number, + _context: ReactServerContext, ... }; export type ReactConsumer = { $$typeof: Symbol | number, - type: ReactContext, + type: ReactContext | ReactServerContext, key: null | string, ref: null, props: { @@ -70,6 +76,26 @@ export type ReactContext = { ... }; +export type ServerContextJSONValue = + | string + | boolean + | number + | null + | $ReadOnlyArray + | {+[key: string]: ServerContextJSONValue}; + +export type ReactServerContext = { + $$typeof: Symbol | number, + Provider: ReactServerProviderType, + _currentValue: T, + _currentValue2: T, + _currentRenderer?: Object | null, + _currentRenderer2?: Object | null, + _threadCount: number, + +displayName: string, + ... +}; + export type ReactPortal = { $$typeof: Symbol | number, key: null | string, diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index a8e1d088664..11af5e6d97a 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -40,7 +40,7 @@ function getWrappedName( } // Keep in sync with react-reconciler/getComponentNameFromFiber -function getContextName(type: ReactContext) { +function getContextName(type: ReactContext | ReactServerContext) { return type.displayName || 'Context'; } diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 6703e2cd365..2c293a02bed 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -403,5 +403,6 @@ "415": "Error parsing the data. It's probably an error code or network corruption.", "416": "This environment don't support binary chunks.", "417": "React currently only supports piping to one writable stream.", - "418": "An error occurred during hydration. The server HTML was replaced with client content" + "418": "An error occurred during hydration. The server HTML was replaced with client content", + "419": "useServerContext is only supported while rendering." } From 29e80db285b22de631ad354128bf14eee41fe66c Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Feb 2022 16:21:14 -0500 Subject: [PATCH 02/83] 1 more test --- .../src/__tests__/ReactFlight-test.js | 20 +++++++++++++++++++ .../src/ReactFiberNewContext.new.js | 1 - .../react-server/src/ReactFlightServer.js | 4 ++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 2db829fd7cd..54ab8aa87bf 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -500,4 +500,24 @@ describe('ReactFlight', () => { expect(Scheduler).toHaveYielded(['ClientBar']); expect(ReactNoop).toMatchRenderedOutput(hi this is server); }); + + it('takes ServerContext from client for refetching usecases', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + function Bar() { + return {React.useServerContext(ServerContext)}; + } + const transport = ReactNoopFlightServer.render(, { + context: [{name: 'ServerContext', value: 'Override'}], + }); + + act(() => { + const flightModel = ReactNoopFlightClient.read(transport); + ReactNoop.render(flightModel); + }); + + expect(ReactNoop).toMatchRenderedOutput(Override); + }); }); diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index e73158d2f77..a871401d9a1 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -92,7 +92,6 @@ export function pushProvider( context: ReactContext | ReactServerContext, nextValue: T, ): void { - debugger; if (isPrimaryRenderer) { push(valueCursor, context._currentValue, providerFiber); diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 649c4f7f35a..e81a2c25ed2 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -38,7 +38,7 @@ import { isModuleReference, } from './ReactFlightServerConfig'; -import {createServerContext} from 'react/src/ReactServerContext'; +import {getOrCreateContextByName} from 'react/src/ReactServerContext'; import { Dispatcher, getCurrentCache, @@ -860,7 +860,7 @@ function importServerContexts( if (contexts) { for (let i = 0; i < contexts.length; i++) { const {name, value} = contexts[i]; - const context = createServerContext(name, null); + const context = getOrCreateContextByName(name); pushProvider(context, value); registry[name] = context; } From 54e881291ea9b8d98bef894beffdb7faf8f5ad84 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Feb 2022 16:31:43 -0500 Subject: [PATCH 03/83] rm unused function --- packages/react-server/src/ReactFlightServer.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index e81a2c25ed2..912793a04da 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -249,10 +249,6 @@ function serializeByRefID(id: number): string { return '@' + id.toString(16); } -function serializeByContextID(id: number): string { - return '!'; -} - function escapeStringValue(value: string): string { if (value[0] === '$' || value[0] === '@') { // We need to escape $ or @ prefixed strings since we use those to encode From e148a0a816f059e56dca69949ecbb87dccca8b06 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 14 Feb 2022 10:45:31 -0500 Subject: [PATCH 04/83] flow+prettier --- .../react-client/src/ReactFlightClient.js | 4 +- .../src/__tests__/ReactFlight-test.js | 359 +++++++++--------- .../react-debug-tools/src/ReactDebugHooks.js | 15 +- .../src/backend/types.js | 6 +- .../src/server/ReactPartialRenderer.js | 19 +- .../src/server/ReactPartialRendererHooks.js | 4 +- packages/react-is/src/ReactIs.js | 2 + .../src/ReactFiberHooks.new.js | 155 +++++--- .../src/ReactFiberHooks.old.js | 155 +++++--- .../src/ReactFiberNewContext.new.js | 8 +- .../src/ReactFiberNewContext.old.js | 8 +- .../src/ReactFiberWorkLoop.old.js | 1 - .../src/ReactInternalTypes.js | 8 +- .../src/getComponentNameFromFiber.js | 8 +- .../ReactFlightDOMRelayServerHostConfig.js | 37 +- .../ReactFlightNativeRelayServerHostConfig.js | 6 +- packages/react-server/src/ReactFlightHooks.js | 9 +- .../react-server/src/ReactFlightServer.js | 115 ++---- .../src/ReactFlightServerConfigStream.js | 5 +- .../src/ReactSuspenseTestUtils.js | 1 + packages/react/src/ReactHooks.js | 1 + packages/react/src/ReactServerContext.js | 49 ++- packages/shared/ReactFeatureFlags.js | 2 + packages/shared/ReactSymbols.js | 2 + packages/shared/ReactTypes.js | 3 +- .../forks/ReactFeatureFlags.native-fb.js | 2 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.testing.js | 1 + .../forks/ReactFeatureFlags.testing.www.js | 2 +- .../shared/forks/ReactFeatureFlags.www.js | 1 + packages/shared/getComponentNameFromType.js | 6 +- scripts/error-codes/codes.json | 3 +- 34 files changed, 583 insertions(+), 417 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index acc3436f7c4..e43446435d5 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -24,7 +24,7 @@ import { parseModel, } from './ReactFlightClientHostConfig'; -import {getOrCreateContextByName} from 'react/src/ReactServerContext'; +import {getOrCreateServerContext} from 'react/src/ReactServerContext'; import { REACT_LAZY_TYPE, @@ -346,7 +346,7 @@ export function parseModelTuple( return createElement(tuple[1], tuple[2], tuple[3]); case REACT_PROVIDER_TYPE: return createElement( - getOrCreateContextByName((tuple[1]: any)).Provider, + getOrCreateServerContext((tuple[1]: any)).Provider, tuple[2], tuple[3], ); diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 54ab8aa87bf..d86e9cad522 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -305,219 +305,232 @@ describe('ReactFlight', () => { ); }); - it('supports basic createServerContext usage', () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'hello from server', - ); - function Foo() { - const context = React.useServerContext(ServerContext); - return
{context}
; - } + describe('ServerContext', () => { + // @gate enableServerContext + it('supports basic createServerContext usage', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'hello from server', + ); + function Foo() { + const context = React.useServerContext(ServerContext); + return
{context}
; + } - const transport = ReactNoopFlightServer.render(); - act(() => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; - ReactNoop.render(ReactNoopFlightClient.read(transport)); + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(
hello from server
); }); - expect(ReactNoop).toMatchRenderedOutput(
hello from server
); - }); + // @gate enableServerContext + it('propagates ServerContext providers in flight', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); - it('propagates ServerContext providers in flight', () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); + function Foo() { + return ( +
+ + + +
+ ); + } + function Bar() { + const context = React.useServerContext(ServerContext); + return context; + } - function Foo() { - return ( -
- - - -
- ); - } - function Bar() { - const context = React.useServerContext(ServerContext); - return context; - } + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); - const transport = ReactNoopFlightServer.render(); - act(() => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; - ReactNoop.render(ReactNoopFlightClient.read(transport)); + expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); }); - expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); - }); - - it('propagates ServerContext and cleansup providers in flight', () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); + // @gate enableServerContext + it('propagates ServerContext and cleansup providers in flight', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); - function Foo() { - return ( - <> - - + function Foo() { + return ( + <> + + + + + + + - + - - - - - - - ); - } - function Bar() { - const context = React.useServerContext(ServerContext); - return {context}; - } - - const transport = ReactNoopFlightServer.render(); - act(() => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; - ReactNoop.render(ReactNoopFlightClient.read(transport)); - }); + + ); + } + function Bar() { + const context = React.useServerContext(ServerContext); + return {context}; + } - expect(ReactNoop).toMatchRenderedOutput( - <> - hi this is server - hi this is server2 - hi this is server outer - hi this is server outer2 - default hello from server - , - ); - }); + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); - it('propagates ServerContext providers in flight after suspending', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); + expect(ReactNoop).toMatchRenderedOutput( + <> + hi this is server + hi this is server2 + hi this is server outer + hi this is server outer2 + default hello from server + , + ); + }); - function Foo() { - return ( -
- - - - - -
+ // @gate enableServerContext + it('propagates ServerContext providers in flight after suspending', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', ); - } - let resolve; - const promise = new Promise(res => { - resolve = () => { - promise.unsuspend = true; - res(); - }; - }); + function Foo() { + return ( +
+ + + + + +
+ ); + } + + let resolve; + const promise = new Promise(res => { + resolve = () => { + promise.unsuspend = true; + res(); + }; + }); - function Bar() { - if (!promise.unsuspend) { - Scheduler.unstable_yieldValue('suspended'); - throw promise; + function Bar() { + if (!promise.unsuspend) { + Scheduler.unstable_yieldValue('suspended'); + throw promise; + } + Scheduler.unstable_yieldValue('rendered'); + const context = React.useServerContext(ServerContext); + return context; } - Scheduler.unstable_yieldValue('rendered'); - const context = React.useServerContext(ServerContext); - return context; - } - const transport = ReactNoopFlightServer.render(); + const transport = ReactNoopFlightServer.render(); - expect(Scheduler).toHaveYielded(['suspended']); + expect(Scheduler).toHaveYielded(['suspended']); - await act(async () => { - resolve(); - await promise; - jest.runAllImmediates(); - }); + await act(async () => { + resolve(); + await promise; + jest.runAllImmediates(); + }); - expect(Scheduler).toHaveYielded(['rendered']); + expect(Scheduler).toHaveYielded(['rendered']); - act(() => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; - ReactNoop.render(ReactNoopFlightClient.read(transport)); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); }); - expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); - }); + // @gate enableServerContext + it('serializes ServerContext to client', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); - it('serializes ServerContext to client', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); + function ClientBar() { + Scheduler.unstable_yieldValue('ClientBar'); + const context = React.useServerContext(ServerContext); + return {context}; + } - function ClientBar() { - Scheduler.unstable_yieldValue('ClientBar'); - const context = React.useServerContext(ServerContext); - return {context}; - } + const Bar = moduleReference(ClientBar); - const Bar = moduleReference(ClientBar); + function Foo() { + return ( + + + + ); + } - function Foo() { - return ( - - - - ); - } + const model = { + foo: , + }; - const model = { - foo: , - }; + const transport = ReactNoopFlightServer.render(model); - const transport = ReactNoopFlightServer.render(model); + expect(Scheduler).toHaveYielded([]); - expect(Scheduler).toHaveYielded([]); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + const flightModel = ReactNoopFlightClient.read(transport); + ReactNoop.render(flightModel.foo); + }); - act(() => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; - const flightModel = ReactNoopFlightClient.read(transport); - ReactNoop.render(flightModel.foo); + expect(Scheduler).toHaveYielded(['ClientBar']); + expect(ReactNoop).toMatchRenderedOutput(hi this is server); }); - expect(Scheduler).toHaveYielded(['ClientBar']); - expect(ReactNoop).toMatchRenderedOutput(hi this is server); - }); + // @gate enableServerContext + it('takes ServerContext from client for refetching usecases', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + function Bar() { + return {React.useServerContext(ServerContext)}; + } + const transport = ReactNoopFlightServer.render(, { + context: [ + { + name: 'ServerContext', + value: 'Override', + }, + ], + }); - it('takes ServerContext from client for refetching usecases', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); - function Bar() { - return {React.useServerContext(ServerContext)}; - } - const transport = ReactNoopFlightServer.render(, { - context: [{name: 'ServerContext', value: 'Override'}], - }); + act(() => { + const flightModel = ReactNoopFlightClient.read(transport); + ReactNoop.render(flightModel); + }); - act(() => { - const flightModel = ReactNoopFlightClient.read(transport); - ReactNoop.render(flightModel); + expect(ReactNoop).toMatchRenderedOutput(Override); }); - - expect(ReactNoop).toMatchRenderedOutput(Override); }); }); diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 4e248371ece..80fc0ec6031 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -346,7 +346,8 @@ function useId(): string { const Dispatcher: DispatcherType = { getCacheForType, - readContext, + // TODO: figure out why flow is complaining here + readContext: (readContext: any), useCacheRefresh, useCallback, useContext, @@ -689,12 +690,16 @@ export function inspectHooks( return buildTree(rootStack, readHookLog, includeHooksSource); } -function setupContexts(contextMap: Map, any>, fiber: Fiber) { +function setupContexts( + contextMap: Map | ReactServerContext, any>, + fiber: Fiber, +) { let current = fiber; while (current) { if (current.tag === ContextProvider) { const providerType: ReactProviderType = current.type; - const context: ReactContext = providerType._context; + const context: ReactContext | ReactServerContext = + providerType._context; if (!contextMap.has(context)) { // Store the current value that we're going to restore later. contextMap.set(context, context._currentValue); @@ -706,7 +711,9 @@ function setupContexts(contextMap: Map, any>, fiber: Fiber) { } } -function restoreContexts(contextMap: Map, any>) { +function restoreContexts( + contextMap: Map | ReactServerContext, any>, +) { contextMap.forEach((value, context) => (context._currentValue = value)); } diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index 28fa7f5a75c..28dd60365c3 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactContext, Wakeable} from 'shared/ReactTypes'; +import type { + ReactContext, + ReactServerContext, + Wakeable, +} from 'shared/ReactTypes'; import type {Source} from 'shared/ReactElementType'; import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; import type { diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 1902dc65690..874c9642dc1 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -10,7 +10,11 @@ import type {ThreadID} from './ReactThreadIDAllocator'; import type {ReactElement} from 'shared/ReactElementType'; import type {LazyComponent} from 'react/src/ReactLazy'; -import type {ReactProvider, ReactContext} from 'shared/ReactTypes'; +import type { + ReactProvider, + ReactContext, + ReactServerContext, +} from 'shared/ReactTypes'; import * as React from 'react'; import isArray from 'shared/isArray'; @@ -776,7 +780,7 @@ class ReactDOMServerRenderer { suspenseDepth: number; contextIndex: number; - contextStack: Array>; + contextStack: Array | ReactServerContext>; contextValueStack: Array; contextProviderStack: ?Array>; // DEV-only @@ -832,9 +836,10 @@ class ReactDOMServerRenderer { * https://github.com/facebook/react/pull/12985#issuecomment-396301248 */ - pushProvider(provider: ReactProvider): void { + pushProvider(provider: ReactProvider): void { const index = ++this.contextIndex; - const context: ReactContext = provider.type._context; + const context: ReactContext | ReactServerContext = + provider.type._context; const threadID = this.threadID; validateContextBounds(context, threadID); const previousValue = context[threadID]; @@ -859,7 +864,8 @@ class ReactDOMServerRenderer { } } - const context: ReactContext = this.contextStack[index]; + const context: ReactContext | ReactServerContext = this + .contextStack[index]; const previousValue = this.contextValueStack[index]; // "Hide" these null assignments from Flow by using `any` @@ -881,7 +887,8 @@ class ReactDOMServerRenderer { clearProviders(): void { // Restore any remaining providers on the stack to previous values for (let index = this.contextIndex; index >= 0; index--) { - const context: ReactContext = this.contextStack[index]; + const context: ReactContext | ReactServerContext = this + .contextStack[index]; const previousValue = this.contextValueStack[index]; context[this.threadID] = previousValue; } diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 1da0127a131..b36cd289caa 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -15,7 +15,7 @@ import type { MutableSourceSubscribeFn, ReactContext, ReactServerContext, - ServerContextValue, + ServerContextJSONValue, } from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; @@ -252,7 +252,7 @@ function useContext(context: ReactContext): T { return context[threadID]; } -function useServerContext( +function useServerContext( context: ReactServerContext, ): T { if (__DEV__) { diff --git a/packages/react-is/src/ReactIs.js b/packages/react-is/src/ReactIs.js index dd81ec03615..8c5d26a08c2 100644 --- a/packages/react-is/src/ReactIs.js +++ b/packages/react-is/src/ReactIs.js @@ -11,6 +11,7 @@ import { REACT_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_TYPE, REACT_ELEMENT_TYPE, REACT_FORWARD_REF_TYPE, REACT_FRAGMENT_TYPE, @@ -43,6 +44,7 @@ export function typeOf(object: any) { const $$typeofType = type && type.$$typeof; switch ($$typeofType) { + case REACT_SERVER_CONTEXT_TYPE: case REACT_CONTEXT_TYPE: case REACT_FORWARD_REF_TYPE: case REACT_LAZY_TYPE: diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index a66ce4a9c3f..7f590186de2 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -33,6 +33,7 @@ import { enableLazyContextPropagation, enableSuspenseLayoutEffectSemantics, enableUseMutableSource, + enableServerContext, } from 'shared/ReactFeatureFlags'; import { @@ -2372,12 +2373,33 @@ function getCacheForType(resourceType: () => T): T { return cacheForType; } +function mountServerContext( + context: ReactServerContext, +): T { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + mountHookTypesDev(); + return readContext(context); +} + +function updateServerContext( + context: ReactServerContext, +): T { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); +} + export const ContextOnlyDispatcher: Dispatcher = { readContext, useCallback: throwInvalidHookError, useContext: throwInvalidHookError, - useServerContext: throwInvalidHookError, useEffect: throwInvalidHookError, useImperativeHandle: throwInvalidHookError, useInsertionEffect: throwInvalidHookError, @@ -2401,12 +2423,15 @@ if (enableCache) { (ContextOnlyDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError; } +if (enableServerContext) { + (ContextOnlyDispatcher: Dispatcher).useServerContext = throwInvalidHookError; +} + const HooksDispatcherOnMount: Dispatcher = { readContext, useCallback: mountCallback, useContext: readContext, - useServerContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, useLayoutEffect: mountLayoutEffect, @@ -2429,13 +2454,15 @@ if (enableCache) { (HooksDispatcherOnMount: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnMount: Dispatcher).useCacheRefresh = mountRefresh; } +if (enableServerContext) { + (HooksDispatcherOnMount: Dispatcher).useServerContext = readContext; +} const HooksDispatcherOnUpdate: Dispatcher = { readContext, useCallback: updateCallback, useContext: readContext, - useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2458,13 +2485,15 @@ if (enableCache) { (HooksDispatcherOnUpdate: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnUpdate: Dispatcher).useCacheRefresh = updateRefresh; } +if (enableServerContext) { + (HooksDispatcherOnUpdate: Dispatcher).useServerContext = readContext; +} const HooksDispatcherOnRerender: Dispatcher = { readContext, useCallback: updateCallback, useContext: readContext, - useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2487,6 +2516,9 @@ if (enableCache) { (HooksDispatcherOnRerender: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnRerender: Dispatcher).useCacheRefresh = updateRefresh; } +if (enableServerContext) { + (HooksDispatcherOnRerender: Dispatcher).useServerContext = readContext; +} let HooksDispatcherOnMountInDEV: Dispatcher | null = null; let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null; @@ -2530,13 +2562,6 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - mountHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2669,6 +2694,9 @@ if (__DEV__) { return mountRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnMountInDEV: Dispatcher).useServerContext = mountServerContext; + } HooksDispatcherOnMountWithHookTypesInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2684,13 +2712,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2818,6 +2839,9 @@ if (__DEV__) { return mountRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useServerContext = updateServerContext; + } HooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2833,13 +2857,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2967,6 +2984,12 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = updateServerContext; + } + + if (!enableServerContext) { + } HooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2983,13 +3006,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3117,6 +3133,9 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + HooksDispatcherOnRerenderInDEV.useServerContext = updateServerContext; + } InvalidNestedHooksDispatcherOnMountInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3135,14 +3154,6 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3285,6 +3296,22 @@ if (__DEV__) { }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return readContext(context); + }; + } + InvalidNestedHooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); @@ -3302,14 +3329,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3451,9 +3470,24 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }; + } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3470,14 +3504,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3619,4 +3645,19 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }; + } } diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 72748ac26d6..3795223bbcc 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -33,6 +33,7 @@ import { enableLazyContextPropagation, enableSuspenseLayoutEffectSemantics, enableUseMutableSource, + enableServerContext, } from 'shared/ReactFeatureFlags'; import { @@ -2372,12 +2373,33 @@ function getCacheForType(resourceType: () => T): T { return cacheForType; } +function mountServerContext( + context: ReactServerContext, +): T { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + mountHookTypesDev(); + return readContext(context); +} + +function updateServerContext( + context: ReactServerContext, +): T { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); +} + export const ContextOnlyDispatcher: Dispatcher = { readContext, useCallback: throwInvalidHookError, useContext: throwInvalidHookError, - useServerContext: throwInvalidHookError, useEffect: throwInvalidHookError, useImperativeHandle: throwInvalidHookError, useInsertionEffect: throwInvalidHookError, @@ -2401,12 +2423,15 @@ if (enableCache) { (ContextOnlyDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError; } +if (enableServerContext) { + (ContextOnlyDispatcher: Dispatcher).useServerContext = throwInvalidHookError; +} + const HooksDispatcherOnMount: Dispatcher = { readContext, useCallback: mountCallback, useContext: readContext, - useServerContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, useLayoutEffect: mountLayoutEffect, @@ -2429,13 +2454,15 @@ if (enableCache) { (HooksDispatcherOnMount: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnMount: Dispatcher).useCacheRefresh = mountRefresh; } +if (enableServerContext) { + (HooksDispatcherOnMount: Dispatcher).useServerContext = readContext; +} const HooksDispatcherOnUpdate: Dispatcher = { readContext, useCallback: updateCallback, useContext: readContext, - useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2458,13 +2485,15 @@ if (enableCache) { (HooksDispatcherOnUpdate: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnUpdate: Dispatcher).useCacheRefresh = updateRefresh; } +if (enableServerContext) { + (HooksDispatcherOnUpdate: Dispatcher).useServerContext = readContext; +} const HooksDispatcherOnRerender: Dispatcher = { readContext, useCallback: updateCallback, useContext: readContext, - useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2487,6 +2516,9 @@ if (enableCache) { (HooksDispatcherOnRerender: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnRerender: Dispatcher).useCacheRefresh = updateRefresh; } +if (enableServerContext) { + (HooksDispatcherOnRerender: Dispatcher).useServerContext = readContext; +} let HooksDispatcherOnMountInDEV: Dispatcher | null = null; let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null; @@ -2530,13 +2562,6 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - mountHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2669,6 +2694,9 @@ if (__DEV__) { return mountRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnMountInDEV: Dispatcher).useServerContext = mountServerContext; + } HooksDispatcherOnMountWithHookTypesInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2684,13 +2712,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2818,6 +2839,9 @@ if (__DEV__) { return mountRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useServerContext = updateServerContext; + } HooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2833,13 +2857,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2967,6 +2984,12 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = updateServerContext; + } + + if (!enableServerContext) { + } HooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2983,13 +3006,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3117,6 +3133,9 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + HooksDispatcherOnRerenderInDEV.useServerContext = updateServerContext; + } InvalidNestedHooksDispatcherOnMountInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3135,14 +3154,6 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3285,6 +3296,22 @@ if (__DEV__) { }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return readContext(context); + }; + } + InvalidNestedHooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); @@ -3302,14 +3329,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3451,9 +3470,24 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }; + } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3470,14 +3504,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3619,4 +3645,19 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }; + } } diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index a871401d9a1..480e86dbfd6 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -129,16 +129,16 @@ export function pushProvider( } } -export function popProvider( - context: ReactContext | ReactServerContext, +export function popProvider( + context: ReactContext | ReactServerContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = (currentValue: any); + context._currentValue = currentValue; } else { - context._currentValue2 = (currentValue: any); + context._currentValue2 = currentValue; } } diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 0adc1d03c6b..78cca5e23b4 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -129,16 +129,16 @@ export function pushProvider( } } -export function popProvider( - context: ReactContext | ReactServerContext, +export function popProvider( + context: ReactContext | ReactServerContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = (currentValue: any); + context._currentValue = currentValue; } else { - context._currentValue2 = (currentValue: any); + context._currentValue2 = currentValue; } } diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index d6e37f2785f..32421e192b1 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -1418,7 +1418,6 @@ function handleError(root, thrownValue): void { ); } } - throwException( root, erroredWork.return, diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 50ad16380c1..c38afe3b714 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -51,14 +51,14 @@ export type HookType = export type ContextDependency = { context: ReactContext | ReactServerContext, - next: ContextDependency | null, + next: ContextDependency | null, memoizedValue: T, ... }; export type Dependencies = { lanes: Lanes, - firstContext: ContextDependency | null, + firstContext: ContextDependency | null, ... }; @@ -323,9 +323,9 @@ export type Dispatcher = {| getSnapshot: MutableSourceGetSnapshotFn, subscribe: MutableSourceSubscribeFn, ): Snapshot, - useServerContext( + useServerContext?: ( context: ReactServerContext, - ): T, + ) => T, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react-reconciler/src/getComponentNameFromFiber.js b/packages/react-reconciler/src/getComponentNameFromFiber.js index 4ae33c2ed59..b7a127450ab 100644 --- a/packages/react-reconciler/src/getComponentNameFromFiber.js +++ b/packages/react-reconciler/src/getComponentNameFromFiber.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; +import type { + ReactContext, + ReactServerContext, + ReactProviderType, +} from 'shared/ReactTypes'; import { FunctionComponent, @@ -52,7 +56,7 @@ function getWrappedName( } // Keep in sync with shared/getComponentNameFromType -function getContextName(type: ReactContext) { +function getContextName(type: ReactContext | ReactServerContext) { return type.displayName || 'Context'; } diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js index 618226a1dc4..85eace536ac 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js @@ -75,12 +75,45 @@ export function processErrorChunk( ]; } +function convertModelToJSON( + request: Request, + parent: {+[key: string]: ReactModel} | $ReadOnlyArray, + key: string, + model: ReactModel, +): JSONValue { + const json = resolveModelToJSON(request, parent, key, model); + if (typeof json === 'object' && json !== null) { + if (isArray(json)) { + const jsonArray: Array = []; + for (let i = 0; i < json.length; i++) { + jsonArray[i] = convertModelToJSON(request, json, '' + i, json[i]); + } + return jsonArray; + } else { + const jsonObj: {[key: string]: JSONValue} = {}; + for (const nextKey in json) { + if (hasOwnProperty.call(json, nextKey)) { + jsonObj[nextKey] = convertModelToJSON( + request, + json, + nextKey, + json[nextKey], + ); + } + } + return jsonObj; + } + } + return json; +} + export function processModelChunk( request: Request, id: number, - jsonValue: JSONValue, + model: ReactModel, ): Chunk { - return ['J', id, jsonValue]; + const json = convertModelToJSON(request, {}, '', model); + return ['J', id, json]; } export function processModuleChunk( diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js index f57017154c3..45f82f3be64 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js @@ -103,13 +103,13 @@ function convertModelToJSON( } return json; } - export function processModelChunk( request: Request, id: number, - jsonValue: JSONValue, + model: ReactModel, ): Chunk { - return ['J', id, jsonValue]; + const json = convertModelToJSON(request, {}, '', model); + return ['J', id, json]; } export function processModuleChunk( diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index 36b37eb4a5d..b87b4b87732 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -9,15 +9,20 @@ import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes'; import type { + ReactContext, ReactServerContext, ServerContextJSONValue, } from 'shared/ReactTypes'; +import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; import {readContext as readContextImpl} from './ReactFlightNewContext'; function readContext( - context: ReactServerContext, + context: ReactContext | ReactServerContext, ): T { if (__DEV__) { + if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { + console.error('Only ServerContext is supported in Flight'); + } if (currentCache === null) { console.error( 'Context can only be read while React is rendering. ' + @@ -27,7 +32,7 @@ function readContext( ); } } - return readContextImpl(context); + return readContextImpl(((context: any): ReactServerContext)); } export const Dispatcher: DispatcherType = { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 912793a04da..ee092d508f0 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -38,7 +38,7 @@ import { isModuleReference, } from './ReactFlightServerConfig'; -import {getOrCreateContextByName} from 'react/src/ReactServerContext'; +import {getOrCreateServerContext} from 'react/src/ReactServerContext'; import { Dispatcher, getCurrentCache, @@ -50,7 +50,6 @@ import { popProvider, switchContext, getActiveContext, - rootContextSnapshot, } from './ReactFlightNewContext'; import { @@ -60,6 +59,7 @@ import { REACT_LAZY_TYPE, REACT_MEMO_TYPE, REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, } from 'shared/ReactSymbols'; import ReactSharedInternals from 'shared/ReactSharedInternals'; @@ -209,7 +209,15 @@ function attemptResolveElement( return attemptResolveElement(type.type, key, ref, props); } case REACT_PROVIDER_TYPE: { - return [REACT_PROVIDER_TYPE, type._context.displayName, key, props]; + parentsWithContextStack.push(type._context); + pushProvider(type._context, props.value); + return [ + REACT_PROVIDER_TYPE, + type._context.displayName, + key, + props, + type._context, + ]; } } } @@ -404,6 +412,23 @@ function describeObjectForErrorMessage( } } +function isReactElement(value: mixed) { + return ( + typeof value === 'object' && + value !== null && + (value: any).$$typeof === REACT_ELEMENT_TYPE + ); +} + +// Save all of the parents/contexts as we recursively stringify onto this stack +// so that we can popContexts as we recurse into neighbors. +const parentsWithContextStack: Array< + | {+[key: string | number]: ReactModel} + | $ReadOnlyArray + | ReactServerContext + | ReactModel, +> = []; + export function resolveModelToJSON( request: Request, parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray, @@ -438,11 +463,7 @@ export function resolveModelToJSON( } // Resolve server components. - while ( - typeof value === 'object' && - value !== null && - value.$$typeof === REACT_ELEMENT_TYPE - ) { + while (isReactElement(value)) { // TODO: Concatenate keys of parents onto children. const element: React$Element = (value: any); try { @@ -453,7 +474,6 @@ export function resolveModelToJSON( element.ref, element.props, ); - 2; } catch (x) { if (typeof x === 'object' && x !== null && typeof x.then === 'function') { // Something suspended, we'll need to create a new segment and resolve it later. @@ -479,6 +499,11 @@ export function resolveModelToJSON( return null; } + if (value.$$typeof === REACT_SERVER_CONTEXT_TYPE && key === '4') { + popProvider((value: any)); + return (undefined: any); + } + if (typeof value === 'object') { if (isModuleReference(value)) { const moduleReference: ModuleReference = (value: any); @@ -557,6 +582,7 @@ export function resolveModelToJSON( } } } + return value; } @@ -703,11 +729,7 @@ function retrySegment(request: Request, segment: Segment): void { switchContext(segment.context); try { let value = segment.model; - while ( - typeof value === 'object' && - value !== null && - value.$$typeof === REACT_ELEMENT_TYPE - ) { + while (isReactElement(value)) { // TODO: Concatenate keys of parents onto children. const element: React$Element = (value: any); // Attempt to render the server component. @@ -720,13 +742,8 @@ function retrySegment(request: Request, segment: Segment): void { element.ref, element.props, ); - 1; } - const processedChunk = processModelChunk( - request, - segment.id, - convertModelToJSON(request, {'': value}, '', value), - ); + const processedChunk = processModelChunk(request, segment.id, value); request.completedJSONChunks.push(processedChunk); } catch (x) { if (typeof x === 'object' && x !== null && typeof x.then === 'function') { @@ -856,7 +873,7 @@ function importServerContexts( if (contexts) { for (let i = 0; i < contexts.length; i++) { const {name, value} = contexts[i]; - const context = getOrCreateContextByName(name); + const context = getOrCreateServerContext(name); pushProvider(context, value); registry[name] = context; } @@ -864,59 +881,3 @@ function importServerContexts( setCurrentServerContexts(registry); return getActiveContext(); } - -function convertModelToJSON( - request: Request, - parent: {+[key: string]: ReactModel} | $ReadOnlyArray, - key: string, - model: ReactModel, -): JSONValue { - const isReactElement = - typeof model === 'object' && - model !== null && - (model: any).$$typeof === REACT_ELEMENT_TYPE; - - let context; - if (isReactElement) { - const element: React$Element = (model: any); - if (element.type.$$typeof === REACT_PROVIDER_TYPE) { - context = element.type._context; - pushProvider(context, element.props.value); - } - } - - const json = resolveModelToJSON(request, parent, key, model); - - if (typeof json === 'object' && json !== null) { - if (isArray(json)) { - const jsonArray: Array = []; - for (let i = 0; i < json.length; i++) { - jsonArray[i] = convertModelToJSON(request, json, '' + i, json[i]); - } - if (context) { - popProvider(context); - } - return jsonArray; - } else { - const jsonObj: {[key: string]: JSONValue} = {}; - for (const nextKey in json) { - if (hasOwnProperty.call(json, nextKey)) { - jsonObj[nextKey] = convertModelToJSON( - request, - json, - nextKey, - json[nextKey], - ); - } - } - if (context) { - popProvider(context); - } - return jsonObj; - } - } - if (context) { - popProvider(context); - } - return json; -} diff --git a/packages/react-server/src/ReactFlightServerConfigStream.js b/packages/react-server/src/ReactFlightServerConfigStream.js index 13cbcf76141..4ff841bfc11 100644 --- a/packages/react-server/src/ReactFlightServerConfigStream.js +++ b/packages/react-server/src/ReactFlightServerConfigStream.js @@ -92,9 +92,10 @@ export function processErrorChunk( export function processModelChunk( request: Request, id: number, - jsonValue: JSONValue, + model: ReactModel, ): Chunk { - const row = serializeRowHeader('J', id) + JSON.stringify(jsonValue) + '\n'; + const json = stringify(model, request.toJSON); + const row = serializeRowHeader('J', id) + json + '\n'; return stringToChunk(row); } diff --git a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js index d75f1f7c9b4..27115bf6b4b 100644 --- a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js +++ b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js @@ -46,6 +46,7 @@ export function waitForSuspense(fn: () => T): Promise { useMutableSource: unsupported, useSyncExternalStore: unsupported, useCacheRefresh: unsupported, + useServerContext: unsupported, }; // Not using async/await because we don't compile it. return new Promise((resolve, reject) => { diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 2c7bc094a1a..1173337ce83 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -189,6 +189,7 @@ export function useServerContext( ): T { // TODO: Warn if regular context is passed in const dispatcher = resolveDispatcher(); + // $FlowFixMe This is unstable, thus optional return dispatcher.useServerContext(Context); } diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 8d4d93ea457..1d15a9b3161 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -7,13 +7,18 @@ * @flow */ -import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; +import { + REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, +} from 'shared/ReactSymbols'; import type { ReactServerContext, ServerContextJSONValue, } from 'shared/ReactTypes'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; + const globalRegistry: { [globalName: string]: ReactServerContext, } = {}; @@ -22,21 +27,40 @@ export function createServerContext( globalName: string, defaultValue: T, ): ReactServerContext { - if (globalRegistry[globalName]) { - throw new Error('ServerContext in that name already exists'); + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + if (!globalRegistry[globalName]) { + globalRegistry[globalName] = _createServerContext(globalName, defaultValue); } + const context = globalRegistry[globalName]; + if (!context._definitionLoaded) { + context._currentValue = defaultValue; + context._currentValue2 = defaultValue; + context._definitionLoaded = true; + } else { + throw new Error(`ServerContext: ${globalName} already defined`); + } + return context; +} + +function _createServerContext( + globalName: string, + defaultValue?: T, +): ReactServerContext { const context: ReactServerContext = { - $$typeof: REACT_CONTEXT_TYPE, + $$typeof: REACT_SERVER_CONTEXT_TYPE, // As a workaround to support multiple concurrent renderers, we categorize // some renderers as primary and others as secondary. We only expect // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - _currentValue: defaultValue, - _currentValue2: defaultValue, + _currentValue: (defaultValue: any), + _currentValue2: (defaultValue: any), // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. _threadCount: 0, + _definitionLoaded: false, // These are circular Provider: (null: any), displayName: globalName, @@ -55,9 +79,14 @@ export function createServerContext( return context; } -export function getOrCreateContextByName(name: string) { - if (!globalRegistry[name]) { - globalRegistry[name] = createServerContext(name, null); +// This function is called by FlightClient to create a server context sent from +// the server. Its possible that FlightClient is creating it before the +// definition is loaded on the server. We'll create it with a null default value +// if thats the case and when the definition loads it will set the correct +// default value. +export function getOrCreateServerContext(globalName: string) { + if (!globalRegistry[globalName]) { + globalRegistry[globalName] = _createServerContext(globalName, undefined); } - return globalRegistry[name]; + return globalRegistry[globalName]; } diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index fe9d77766cd..361d1012d80 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -191,6 +191,8 @@ export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = true; +export const enableServerContext = false; + // Only enabled in www builds export const enableUseMutableSource = false; diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index a50a06b1501..66593062ef3 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -20,6 +20,7 @@ export let REACT_STRICT_MODE_TYPE = 0xeacc; export let REACT_PROFILER_TYPE = 0xead2; export let REACT_PROVIDER_TYPE = 0xeacd; export let REACT_CONTEXT_TYPE = 0xeace; +export let REACT_SERVER_CONTEXT_TYPE = 0xeacf; export let REACT_FORWARD_REF_TYPE = 0xead0; export let REACT_SUSPENSE_TYPE = 0xead1; export let REACT_SUSPENSE_LIST_TYPE = 0xead8; @@ -40,6 +41,7 @@ if (typeof Symbol === 'function' && Symbol.for) { REACT_PROFILER_TYPE = symbolFor('react.profiler'); REACT_PROVIDER_TYPE = symbolFor('react.provider'); REACT_CONTEXT_TYPE = symbolFor('react.context'); + REACT_SERVER_CONTEXT_TYPE = symbolFor('react.server_context'); REACT_FORWARD_REF_TYPE = symbolFor('react.forward_ref'); REACT_SUSPENSE_TYPE = symbolFor('react.suspense'); REACT_SUSPENSE_LIST_TYPE = symbolFor('react.suspense_list'); diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 9bfeca29dcb..fc400a409f2 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -25,7 +25,7 @@ export type ReactText = string | number; export type ReactProvider = { $$typeof: Symbol | number, - type: ReactProviderType, + type: ReactProviderType | ReactServerProviderType, key: null | string, ref: null, props: { @@ -92,6 +92,7 @@ export type ReactServerContext = { _currentRenderer?: Object | null, _currentRenderer2?: Object | null, _threadCount: number, + _definitionLoaded: boolean, +displayName: string, ... }; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 699d9a571ff..a19447b82a1 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -75,6 +75,8 @@ export const allowConcurrentByDefault = true; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = false; + export const enableUseMutableSource = true; export const enableTransitionTracing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 0328a98aef5..2a7f42150d7 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -67,6 +67,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = false; export const enableUseMutableSource = false; export const enableTransitionTracing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index b1afbda8ec5..26d690d69d3 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -67,6 +67,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = true; export const enableUseMutableSource = false; export const enableTransitionTracing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index e64d09d5d66..b853c430146 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -67,6 +67,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = false; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index f0d4a9acc56..844aa0a92fa 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -67,6 +67,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = true; export const enableUseMutableSource = false; export const enableTransitionTracing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index 43ce351301c..0be032ae667 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -67,7 +67,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; - +export const enableServerContext = false; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index c0545d2aacf..44e3d1887f2 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -100,6 +100,7 @@ export const deletedTreeCleanUpLevel = 3; export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = true; +export const enableServerContext = false; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 11af5e6d97a..8b202cd2a2f 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -8,7 +8,11 @@ */ import type {LazyComponent} from 'react/src/ReactLazy'; -import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; +import type { + ReactContext, + ReactServerContext, + ReactProviderType, +} from 'shared/ReactTypes'; import { REACT_CONTEXT_TYPE, diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 2c293a02bed..b46f6ce6df4 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -404,5 +404,6 @@ "416": "This environment don't support binary chunks.", "417": "React currently only supports piping to one writable stream.", "418": "An error occurred during hydration. The server HTML was replaced with client content", - "419": "useServerContext is only supported while rendering." + "419": "useServerContext is only supported while rendering.", + "420": "ServerContext: %s already defined" } From 532dfa28d4244dd278425778cbf90b05cd2a2064 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 14 Feb 2022 10:50:40 -0500 Subject: [PATCH 05/83] flow again =) --- packages/react-reconciler/src/ReactFiberHooks.new.js | 2 +- packages/react-reconciler/src/ReactFiberHooks.old.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 7f590186de2..8f535d46923 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -3134,7 +3134,7 @@ if (__DEV__) { }; } if (enableServerContext) { - HooksDispatcherOnRerenderInDEV.useServerContext = updateServerContext; + (HooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = updateServerContext; } InvalidNestedHooksDispatcherOnMountInDEV = { diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 3795223bbcc..3069d5bc9f1 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -3134,7 +3134,7 @@ if (__DEV__) { }; } if (enableServerContext) { - HooksDispatcherOnRerenderInDEV.useServerContext = updateServerContext; + (HooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = updateServerContext; } InvalidNestedHooksDispatcherOnMountInDEV = { From f2868c2ba8ff719cfd17e88064a3f37eef81714a Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Tue, 15 Feb 2022 13:53:17 -0500 Subject: [PATCH 06/83] duplicate ReactServerContext across packages --- .../react-client/src/ReactFlightClient.js | 2 +- .../src/ReactFlightClientStream.js | 1 + .../react-client/src/ReactServerContext.js | 99 +++++++++++++++++++ .../src/__tests__/ReactFlight-test.js | 7 ++ .../src/ReactFiberBeginWork.new.js | 1 + .../react-server/src/ReactFlightServer.js | 2 +- .../react-server/src/ReactServerContext.js | 98 ++++++++++++++++++ packages/react/src/ReactServerContext.js | 27 +++-- .../react/src/ReactServerContextRegistry.js | 5 + packages/react/src/ReactSharedInternals.js | 6 ++ .../shared/forks/ReactFeatureFlags.www.js | 2 +- 11 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 packages/react-client/src/ReactServerContext.js create mode 100644 packages/react-server/src/ReactServerContext.js create mode 100644 packages/react/src/ReactServerContextRegistry.js diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index e43446435d5..90a960e3a61 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -24,7 +24,7 @@ import { parseModel, } from './ReactFlightClientHostConfig'; -import {getOrCreateServerContext} from 'react/src/ReactServerContext'; +import {getOrCreateServerContext} from './ReactServerContext'; import { REACT_LAZY_TYPE, diff --git a/packages/react-client/src/ReactFlightClientStream.js b/packages/react-client/src/ReactFlightClientStream.js index 9f07d8cc999..e54f2adb69e 100644 --- a/packages/react-client/src/ReactFlightClientStream.js +++ b/packages/react-client/src/ReactFlightClientStream.js @@ -46,6 +46,7 @@ function processFullRow(response: Response, row: string): void { return; } case 'M': { + debugger; resolveModule(response, id, text); return; } diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js new file mode 100644 index 00000000000..42639302ac3 --- /dev/null +++ b/packages/react-client/src/ReactServerContext.js @@ -0,0 +1,99 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import { + REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, +} from 'shared/ReactSymbols'; + +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; + +import ReactSharedInternals from 'shared/ReactSharedInternals'; + +import {enableServerContext} from 'shared/ReactFeatureFlags'; + +const globalServerContextRegistry = + ReactSharedInternals.globalServerContextRegistry; + +export function createServerContext( + globalName: string, + defaultValue: T, +): ReactServerContext { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + defaultValue, + ); + } + const context = globalServerContextRegistry[globalName]; + if (!context._definitionLoaded) { + context._currentValue = defaultValue; + context._currentValue2 = defaultValue; + context._definitionLoaded = true; + } else { + throw new Error(`ServerContext: ${globalName} already defined`); + } + return context; +} + +function _createServerContext( + globalName: string, + defaultValue?: T, +): ReactServerContext { + const context: ReactServerContext = { + $$typeof: REACT_SERVER_CONTEXT_TYPE, + // As a workaround to support multiple concurrent renderers, we categorize + // some renderers as primary and others as secondary. We only expect + // there to be two concurrent renderers at most: React Native (primary) and + // Fabric (secondary); React DOM (primary) and React ART (secondary). + // Secondary renderers store their context values on separate fields. + _currentValue: (defaultValue: any), + _currentValue2: (defaultValue: any), + // Used to track how many concurrent renderers this context currently + // supports within in a single renderer. Such as parallel server rendering. + _threadCount: 0, + _definitionLoaded: false, + // These are circular + Provider: (null: any), + displayName: globalName, + }; + + context.Provider = { + $$typeof: REACT_PROVIDER_TYPE, + _context: context, + }; + + if (__DEV__) { + context._currentRenderer = null; + context._currentRenderer2 = null; + } + globalServerContextRegistry[globalName] = context; + return context; +} + +// This function is called by FlightClient to create a server context sent from +// the server. Its possible that FlightClient is creating it before the +// definition is loaded on the server. We'll create it with a null default value +// if thats the case and when the definition loads it will set the correct +// default value. +export function getOrCreateServerContext(globalName: string) { + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + undefined, + ); + } + return globalServerContextRegistry[globalName]; +} diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index d86e9cad522..683e4c2a3e2 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -505,6 +505,13 @@ describe('ReactFlight', () => { expect(Scheduler).toHaveYielded(['ClientBar']); expect(ReactNoop).toMatchRenderedOutput(hi this is server); + + expect(() => { + const ServerContext2 = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + }).toThrow('ServerContext: ServerContext already defined'); }); // @gate enableServerContext diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 6cf1c3f0e7e..3dc563f9635 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -1531,6 +1531,7 @@ function mountLazyComponent( hint = ' Did you wrap a component in React.lazy() more than once?'; } } + debugger; // This message intentionally doesn't mention ForwardRef or MemoComponent // because the fact that it's a separate type of work is an diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index ee092d508f0..f6a3bf3e810 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -38,7 +38,7 @@ import { isModuleReference, } from './ReactFlightServerConfig'; -import {getOrCreateServerContext} from 'react/src/ReactServerContext'; +import {getOrCreateServerContext} from './ReactServerContext'; import { Dispatcher, getCurrentCache, diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js new file mode 100644 index 00000000000..b9e48c085ad --- /dev/null +++ b/packages/react-server/src/ReactServerContext.js @@ -0,0 +1,98 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import { + REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, +} from 'shared/ReactSymbols'; + +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; + +import {enableServerContext} from 'shared/ReactFeatureFlags'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; + +const globalServerContextRegistry = + ReactSharedInternals.globalServerContextRegistry; + +export function createServerContext( + globalName: string, + defaultValue: T, +): ReactServerContext { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + defaultValue, + ); + } + const context = globalServerContextRegistry[globalName]; + if (!context._definitionLoaded) { + context._currentValue = defaultValue; + context._currentValue2 = defaultValue; + context._definitionLoaded = true; + } else { + throw new Error(`ServerContext: ${globalName} already defined`); + } + return context; +} + +function _createServerContext( + globalName: string, + defaultValue?: T, +): ReactServerContext { + const context: ReactServerContext = { + $$typeof: REACT_SERVER_CONTEXT_TYPE, + // As a workaround to support multiple concurrent renderers, we categorize + // some renderers as primary and others as secondary. We only expect + // there to be two concurrent renderers at most: React Native (primary) and + // Fabric (secondary); React DOM (primary) and React ART (secondary). + // Secondary renderers store their context values on separate fields. + _currentValue: (defaultValue: any), + _currentValue2: (defaultValue: any), + // Used to track how many concurrent renderers this context currently + // supports within in a single renderer. Such as parallel server rendering. + _threadCount: 0, + _definitionLoaded: false, + // These are circular + Provider: (null: any), + displayName: globalName, + }; + + context.Provider = { + $$typeof: REACT_PROVIDER_TYPE, + _context: context, + }; + + if (__DEV__) { + context._currentRenderer = null; + context._currentRenderer2 = null; + } + globalServerContextRegistry[globalName] = context; + return context; +} + +// This function is called by FlightClient to create a server context sent from +// the server. Its possible that FlightClient is creating it before the +// definition is loaded on the server. We'll create it with a null default value +// if thats the case and when the definition loads it will set the correct +// default value. +export function getOrCreateServerContext(globalName: string) { + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + undefined, + ); + } + return globalServerContextRegistry[globalName]; +} diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 1d15a9b3161..42639302ac3 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -17,11 +17,12 @@ import type { ServerContextJSONValue, } from 'shared/ReactTypes'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; + import {enableServerContext} from 'shared/ReactFeatureFlags'; -const globalRegistry: { - [globalName: string]: ReactServerContext, -} = {}; +const globalServerContextRegistry = + ReactSharedInternals.globalServerContextRegistry; export function createServerContext( globalName: string, @@ -30,10 +31,13 @@ export function createServerContext( if (!enableServerContext) { throw new Error('Not implemented.'); } - if (!globalRegistry[globalName]) { - globalRegistry[globalName] = _createServerContext(globalName, defaultValue); + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + defaultValue, + ); } - const context = globalRegistry[globalName]; + const context = globalServerContextRegistry[globalName]; if (!context._definitionLoaded) { context._currentValue = defaultValue; context._currentValue2 = defaultValue; @@ -75,7 +79,7 @@ function _createServerContext( context._currentRenderer = null; context._currentRenderer2 = null; } - globalRegistry[globalName] = context; + globalServerContextRegistry[globalName] = context; return context; } @@ -85,8 +89,11 @@ function _createServerContext( // if thats the case and when the definition loads it will set the correct // default value. export function getOrCreateServerContext(globalName: string) { - if (!globalRegistry[globalName]) { - globalRegistry[globalName] = _createServerContext(globalName, undefined); + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + undefined, + ); } - return globalRegistry[globalName]; + return globalServerContextRegistry[globalName]; } diff --git a/packages/react/src/ReactServerContextRegistry.js b/packages/react/src/ReactServerContextRegistry.js new file mode 100644 index 00000000000..c98f12b6e35 --- /dev/null +++ b/packages/react/src/ReactServerContextRegistry.js @@ -0,0 +1,5 @@ +import type {ReactServerContext} from 'shared/ReactTypes'; + +export const globalServerContextRegistry: { + [globalName: string]: ReactServerContext, +} = {}; diff --git a/packages/react/src/ReactSharedInternals.js b/packages/react/src/ReactSharedInternals.js index 3443fb27663..de3cc15e724 100644 --- a/packages/react/src/ReactSharedInternals.js +++ b/packages/react/src/ReactSharedInternals.js @@ -11,6 +11,8 @@ import ReactCurrentBatchConfig from './ReactCurrentBatchConfig'; import ReactCurrentActQueue from './ReactCurrentActQueue'; import ReactCurrentOwner from './ReactCurrentOwner'; import ReactDebugCurrentFrame from './ReactDebugCurrentFrame'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; +import {globalServerContextRegistry} from './ReactServerContextRegistry'; const ReactSharedInternals = { ReactCurrentDispatcher, @@ -25,4 +27,8 @@ if (__DEV__) { ReactSharedInternals.ReactCurrentActQueue = ReactCurrentActQueue; } +if (enableServerContext) { + ReactSharedInternals.globalServerContextRegistry = globalServerContextRegistry; +} + export default ReactSharedInternals; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 44e3d1887f2..1012db58d69 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -100,7 +100,7 @@ export const deletedTreeCleanUpLevel = 3; export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = true; -export const enableServerContext = false; +export const enableServerContext = true; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; From a86149262176a2f21d5cfbabf9836b7328d28a56 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 20:10:07 -0500 Subject: [PATCH 07/83] store default value when lazily initializing server context --- .../src/ReactFlightClientStream.js | 1 - .../react-client/src/ReactServerContext.js | 55 ++++++-- .../src/__tests__/ReactFlight-test.js | 117 ++++++++++++++++-- .../src/ReactFiberBeginWork.new.js | 1 - .../react-server/src/ReactFlightServer.js | 18 +-- .../react-server/src/ReactServerContext.js | 58 +++++++-- packages/react/src/ReactServerContext.js | 55 ++++++-- .../react/src/ReactServerContextRegistry.js | 4 +- packages/shared/ReactTypes.js | 3 + 9 files changed, 265 insertions(+), 47 deletions(-) diff --git a/packages/react-client/src/ReactFlightClientStream.js b/packages/react-client/src/ReactFlightClientStream.js index e54f2adb69e..9f07d8cc999 100644 --- a/packages/react-client/src/ReactFlightClientStream.js +++ b/packages/react-client/src/ReactFlightClientStream.js @@ -46,7 +46,6 @@ function processFullRow(response: Response, row: string): void { return; } case 'M': { - debugger; resolveModule(response, id, text); return; } diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index 42639302ac3..8d4d6a940fc 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -24,6 +24,8 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; +const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; + export function createServerContext( globalName: string, defaultValue: T, @@ -39,9 +41,8 @@ export function createServerContext( } const context = globalServerContextRegistry[globalName]; if (!context._definitionLoaded) { - context._currentValue = defaultValue; - context._currentValue2 = defaultValue; context._definitionLoaded = true; + context._defaultValue = defaultValue; } else { throw new Error(`ServerContext: ${globalName} already defined`); } @@ -50,7 +51,7 @@ export function createServerContext( function _createServerContext( globalName: string, - defaultValue?: T, + defaultValue: T, ): ReactServerContext { const context: ReactServerContext = { $$typeof: REACT_SERVER_CONTEXT_TYPE, @@ -59,8 +60,48 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - _currentValue: (defaultValue: any), - _currentValue2: (defaultValue: any), + __currentValue: defaultValue, + __currentValue2: defaultValue, + + get _currentValue() { + const value = context.__currentValue; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + } + return value; + }, + + set _currentValue(value) { + context.__currentValue = value; + }, + + get _currentValue2() { + const value = context.__currentValue2; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + return (undefined: any); + } + return value; + }, + + set _currentValue2(value) { + context.__currentValue2 = value; + }, + + _defaultValue: (undefined: any), + // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. _threadCount: 0, @@ -88,11 +129,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string) { +export function getOrCreateServerContext(globalName: string, value: any) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - undefined, + value === undefined ? DEFAULT_PLACEHOLDER : value, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 683e4c2a3e2..dae8d4d5858 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -18,6 +18,7 @@ let ReactNoopFlightClient; let ErrorBoundary; let NoErrorExpected; let Scheduler; +let globalServerContextRegistry; describe('ReactFlight', () => { beforeEach(() => { @@ -29,6 +30,9 @@ describe('ReactFlight', () => { ReactNoopFlightClient = require('react-noop-renderer/flight-client'); act = require('jest-react').act; Scheduler = require('scheduler'); + const ReactSharedInternals = require('shared/ReactSharedInternals').default; + globalServerContextRegistry = + ReactSharedInternals.globalServerContextRegistry; ErrorBoundary = class extends React.Component { state = {hasError: false, error: null}; @@ -331,7 +335,7 @@ describe('ReactFlight', () => { it('propagates ServerContext providers in flight', () => { const ServerContext = React.createServerContext( 'ServerContext', - 'default hello from server', + 'default', ); function Foo() { @@ -362,7 +366,7 @@ describe('ReactFlight', () => { it('propagates ServerContext and cleansup providers in flight', () => { const ServerContext = React.createServerContext( 'ServerContext', - 'default hello from server', + 'default', ); function Foo() { @@ -402,7 +406,7 @@ describe('ReactFlight', () => { hi this is server2 hi this is server outer hi this is server outer2 - default hello from server + default , ); }); @@ -411,7 +415,7 @@ describe('ReactFlight', () => { it('propagates ServerContext providers in flight after suspending', async () => { const ServerContext = React.createServerContext( 'ServerContext', - 'default hello from server', + 'default', ); function Foo() { @@ -469,7 +473,7 @@ describe('ReactFlight', () => { it('serializes ServerContext to client', async () => { const ServerContext = React.createServerContext( 'ServerContext', - 'default hello from server', + 'default', ); function ClientBar() { @@ -507,10 +511,7 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(hi this is server); expect(() => { - const ServerContext2 = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); + React.createServerContext('ServerContext', 'default'); }).toThrow('ServerContext: ServerContext already defined'); }); @@ -518,7 +519,7 @@ describe('ReactFlight', () => { it('takes ServerContext from client for refetching usecases', async () => { const ServerContext = React.createServerContext( 'ServerContext', - 'default hello from server', + 'default', ); function Bar() { return {React.useServerContext(ServerContext)}; @@ -539,5 +540,101 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(Override); }); + + // @gate enableServerContext + it('sets default initial value when defined lazily on server or client', async () => { + let ServerContext; + function inlineLazyServerContextInitialization() { + if (!ServerContext) { + ServerContext = React.createServerContext('ServerContext', 'default'); + } + return ServerContext; + } + + let ClientContext; + function inlineContextInitialization() { + if (!ClientContext) { + ClientContext = React.createServerContext('ServerContext', 'default'); + } + return ClientContext; + } + + function ClientBaz() { + const context = inlineContextInitialization(); + const value = React.useServerContext(context); + return
{value}
; + } + + const Baz = moduleReference(ClientBaz); + + function Bar() { + return ( +
+
+ {React.useServerContext(inlineLazyServerContextInitialization())} +
+ +
+ ); + } + + function ServerApp() { + const Context = inlineLazyServerContextInitialization(); + return ( + <> + + + + + + ); + } + + function ClientApp({serverModel}) { + return ( + <> + {serverModel} + + + ); + } + + const transport = ReactNoopFlightServer.render(, { + context: [ + { + name: 'ServerContext', + value: 'Override', + }, + ], + }); + + expect(ClientContext).toBe(undefined); + act(() => { + delete globalServerContextRegistry.ServerContext; + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + const serverModel = ReactNoopFlightClient.read(transport); + ReactNoop.render(); + }); + + expect(ReactNoop).toMatchRenderedOutput( + <> +
+
test
+
test
+
+
+
Override
+ + {/** In practice this would also be Override because the */} + {/** server context sent up to the server would be around this*/} + {/** tree. For this test we didn't do that though so it uses the */} + {/** real default */} +
default
+
+
default
+ , + ); + }); }); }); diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 3dc563f9635..6cf1c3f0e7e 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -1531,7 +1531,6 @@ function mountLazyComponent( hint = ' Did you wrap a component in React.lazy() more than once?'; } } - debugger; // This message intentionally doesn't mention ForwardRef or MemoComponent // because the fact that it's a separate type of work is an diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index f6a3bf3e810..460385be089 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -209,7 +209,6 @@ function attemptResolveElement( return attemptResolveElement(type.type, key, ref, props); } case REACT_PROVIDER_TYPE: { - parentsWithContextStack.push(type._context); pushProvider(type._context, props.value); return [ REACT_PROVIDER_TYPE, @@ -420,15 +419,6 @@ function isReactElement(value: mixed) { ); } -// Save all of the parents/contexts as we recursively stringify onto this stack -// so that we can popContexts as we recurse into neighbors. -const parentsWithContextStack: Array< - | {+[key: string | number]: ReactModel} - | $ReadOnlyArray - | ReactServerContext - | ReactModel, -> = []; - export function resolveModelToJSON( request: Request, parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray, @@ -499,7 +489,11 @@ export function resolveModelToJSON( return null; } - if (value.$$typeof === REACT_SERVER_CONTEXT_TYPE && key === '4') { + if ( + value.$$typeof === REACT_SERVER_CONTEXT_TYPE && + key === '4' && + parent[0] === REACT_PROVIDER_TYPE + ) { popProvider((value: any)); return (undefined: any); } @@ -873,7 +867,7 @@ function importServerContexts( if (contexts) { for (let i = 0; i < contexts.length; i++) { const {name, value} = contexts[i]; - const context = getOrCreateServerContext(name); + const context = getOrCreateServerContext(name, value); pushProvider(context, value); registry[name] = context; } diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index b9e48c085ad..8d4d6a940fc 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -17,12 +17,15 @@ import type { ServerContextJSONValue, } from 'shared/ReactTypes'; -import {enableServerContext} from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; + const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; +const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; + export function createServerContext( globalName: string, defaultValue: T, @@ -38,9 +41,8 @@ export function createServerContext( } const context = globalServerContextRegistry[globalName]; if (!context._definitionLoaded) { - context._currentValue = defaultValue; - context._currentValue2 = defaultValue; context._definitionLoaded = true; + context._defaultValue = defaultValue; } else { throw new Error(`ServerContext: ${globalName} already defined`); } @@ -49,7 +51,7 @@ export function createServerContext( function _createServerContext( globalName: string, - defaultValue?: T, + defaultValue: T, ): ReactServerContext { const context: ReactServerContext = { $$typeof: REACT_SERVER_CONTEXT_TYPE, @@ -58,8 +60,48 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - _currentValue: (defaultValue: any), - _currentValue2: (defaultValue: any), + __currentValue: defaultValue, + __currentValue2: defaultValue, + + get _currentValue() { + const value = context.__currentValue; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + } + return value; + }, + + set _currentValue(value) { + context.__currentValue = value; + }, + + get _currentValue2() { + const value = context.__currentValue2; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + return (undefined: any); + } + return value; + }, + + set _currentValue2(value) { + context.__currentValue2 = value; + }, + + _defaultValue: (undefined: any), + // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. _threadCount: 0, @@ -87,11 +129,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string) { +export function getOrCreateServerContext(globalName: string, value: any) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - undefined, + value === undefined ? DEFAULT_PLACEHOLDER : value, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 42639302ac3..8d4d6a940fc 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -24,6 +24,8 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; +const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; + export function createServerContext( globalName: string, defaultValue: T, @@ -39,9 +41,8 @@ export function createServerContext( } const context = globalServerContextRegistry[globalName]; if (!context._definitionLoaded) { - context._currentValue = defaultValue; - context._currentValue2 = defaultValue; context._definitionLoaded = true; + context._defaultValue = defaultValue; } else { throw new Error(`ServerContext: ${globalName} already defined`); } @@ -50,7 +51,7 @@ export function createServerContext( function _createServerContext( globalName: string, - defaultValue?: T, + defaultValue: T, ): ReactServerContext { const context: ReactServerContext = { $$typeof: REACT_SERVER_CONTEXT_TYPE, @@ -59,8 +60,48 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - _currentValue: (defaultValue: any), - _currentValue2: (defaultValue: any), + __currentValue: defaultValue, + __currentValue2: defaultValue, + + get _currentValue() { + const value = context.__currentValue; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + } + return value; + }, + + set _currentValue(value) { + context.__currentValue = value; + }, + + get _currentValue2() { + const value = context.__currentValue2; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + return (undefined: any); + } + return value; + }, + + set _currentValue2(value) { + context.__currentValue2 = value; + }, + + _defaultValue: (undefined: any), + // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. _threadCount: 0, @@ -88,11 +129,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string) { +export function getOrCreateServerContext(globalName: string, value: any) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - undefined, + value === undefined ? DEFAULT_PLACEHOLDER : value, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react/src/ReactServerContextRegistry.js b/packages/react/src/ReactServerContextRegistry.js index c98f12b6e35..f24d9474309 100644 --- a/packages/react/src/ReactServerContextRegistry.js +++ b/packages/react/src/ReactServerContextRegistry.js @@ -2,4 +2,6 @@ import type {ReactServerContext} from 'shared/ReactTypes'; export const globalServerContextRegistry: { [globalName: string]: ReactServerContext, -} = {}; +} = { + __defaultValue: {}, +}; diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index fc400a409f2..895d86d5e30 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -87,6 +87,9 @@ export type ServerContextJSONValue = export type ReactServerContext = { $$typeof: Symbol | number, Provider: ReactServerProviderType, + _defaultValue: T, + __currentValue: T, + __currentValue2: T, _currentValue: T, _currentValue2: T, _currentRenderer?: Object | null, From fb521be23dfd3962444d85b5392544d75770c355 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 20:15:38 -0500 Subject: [PATCH 08/83] . --- packages/react-client/src/ReactServerContext.js | 2 +- packages/react-server/src/ReactServerContext.js | 2 +- packages/react/src/ReactServerContext.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index 8d4d6a940fc..2cab7790b5f 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -70,7 +70,7 @@ function _createServerContext( // and we should use the default value in the definition. // Otherwise the definition hasn't loaded so `useServerContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; } } diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index 8d4d6a940fc..2cab7790b5f 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -70,7 +70,7 @@ function _createServerContext( // and we should use the default value in the definition. // Otherwise the definition hasn't loaded so `useServerContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; } } diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 8d4d6a940fc..2cab7790b5f 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -70,7 +70,7 @@ function _createServerContext( // and we should use the default value in the definition. // Otherwise the definition hasn't loaded so `useServerContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; } } From a7051aa673f23771ef07002c864f85745ea06705 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 20:19:31 -0500 Subject: [PATCH 09/83] better comment --- .../react-client/src/ReactServerContext.js | 18 +++++++++--------- .../react-server/src/ReactServerContext.js | 18 +++++++++--------- packages/react/src/ReactServerContext.js | 18 +++++++++--------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index 2cab7790b5f..a5064da544f 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -66,12 +66,12 @@ function _createServerContext( get _currentValue() { const value = context.__currentValue; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } } return value; @@ -84,12 +84,12 @@ function _createServerContext( get _currentValue2() { const value = context.__currentValue2; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } return (undefined: any); } diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index 2cab7790b5f..a5064da544f 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -66,12 +66,12 @@ function _createServerContext( get _currentValue() { const value = context.__currentValue; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } } return value; @@ -84,12 +84,12 @@ function _createServerContext( get _currentValue2() { const value = context.__currentValue2; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } return (undefined: any); } diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 2cab7790b5f..a5064da544f 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -66,12 +66,12 @@ function _createServerContext( get _currentValue() { const value = context.__currentValue; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } } return value; @@ -84,12 +84,12 @@ function _createServerContext( get _currentValue2() { const value = context.__currentValue2; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } return (undefined: any); } From 066b2068a5cfa274a6f2acc6e61cf2f66fa65a82 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 20:42:02 -0500 Subject: [PATCH 10/83] derp... missing import --- packages/react-client/src/ReactServerContext.js | 5 ++++- packages/react-server/src/ReactServerContext.js | 5 ++++- packages/react/src/ReactServerContext.js | 5 ++++- packages/react/src/forks/ReactSharedInternals.umd.js | 6 ++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index a5064da544f..caa3cb381c2 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -24,7 +24,10 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +let DEFAULT_PLACEHOLDER; +if (enableServerContext) { + DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +} export function createServerContext( globalName: string, diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index a5064da544f..caa3cb381c2 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -24,7 +24,10 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +let DEFAULT_PLACEHOLDER; +if (enableServerContext) { + DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +} export function createServerContext( globalName: string, diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index a5064da544f..caa3cb381c2 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -24,7 +24,10 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +let DEFAULT_PLACEHOLDER; +if (enableServerContext) { + DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +} export function createServerContext( globalName: string, diff --git a/packages/react/src/forks/ReactSharedInternals.umd.js b/packages/react/src/forks/ReactSharedInternals.umd.js index 5dcfbb5650e..87dd07bd0a7 100644 --- a/packages/react/src/forks/ReactSharedInternals.umd.js +++ b/packages/react/src/forks/ReactSharedInternals.umd.js @@ -12,6 +12,8 @@ import ReactCurrentActQueue from '../ReactCurrentActQueue'; import ReactCurrentOwner from '../ReactCurrentOwner'; import ReactDebugCurrentFrame from '../ReactDebugCurrentFrame'; import ReactCurrentBatchConfig from '../ReactCurrentBatchConfig'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; +import {globalServerContextRegistry} from '../ReactServerContextRegistry'; const ReactSharedInternals = { ReactCurrentDispatcher, @@ -33,4 +35,8 @@ if (__DEV__) { ReactSharedInternals.ReactDebugCurrentFrame = ReactDebugCurrentFrame; } +if (enableServerContext) { + ReactSharedInternals.globalServerContextRegistry = globalServerContextRegistry; +} + export default ReactSharedInternals; From 7706ed72f46e051e45de5f50e4512d44f92f72f9 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 20:53:20 -0500 Subject: [PATCH 11/83] rm optional chaining --- packages/react-server/src/ReactFlightServer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 460385be089..e292bbac0ab 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -131,7 +131,7 @@ export function createRequest( options?: RequestOptions, ): Request { const pingedSegments = []; - const onError = options?.onError; + const onError = options ? options.onError : undefined; const request = { status: OPEN, fatalError: null, @@ -152,7 +152,7 @@ export function createRequest( }, }; request.pendingChunks++; - const context = createRootContext(options?.context); + const context = createRootContext(options ? options.context : undefined); const rootSegment = createSegment(request, model, context); pingedSegments.push(rootSegment); return request; From 8bb4359ffeab3da70343f95d44c08bccbcd00784 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 21:13:46 -0500 Subject: [PATCH 12/83] missed feature flag --- packages/shared/forks/ReactFeatureFlags.test-renderer.native.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index 6c1df25af24..718765d31e6 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -65,6 +65,7 @@ export const allowConcurrentByDefault = true; export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = false; export const enableUseMutableSource = false; export const enableTransitionTracing = false; From 03f08ba1c67d83ffe98a2c3ad7ddd8d0d9583374 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 17 Feb 2022 07:03:49 -0500 Subject: [PATCH 13/83] React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED ?? --- packages/react-client/src/__tests__/ReactFlight-test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index dae8d4d5858..54fc5e5bb22 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -30,7 +30,8 @@ describe('ReactFlight', () => { ReactNoopFlightClient = require('react-noop-renderer/flight-client'); act = require('jest-react').act; Scheduler = require('scheduler'); - const ReactSharedInternals = require('shared/ReactSharedInternals').default; + const ReactSharedInternals = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; From 9948c4394daae53fb0b44af26372daa5d8f5878b Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 17 Feb 2022 09:58:46 -0500 Subject: [PATCH 14/83] add warning if non ServerContext passed into useServerContext --- packages/react-debug-tools/src/ReactDebugHooks.js | 3 +-- packages/react-server/src/ReactFlightHooks.js | 11 ----------- packages/react-server/src/ReactFlightServer.js | 8 +------- packages/react/src/ReactHooks.js | 10 +++++++++- packages/shared/forks/ReactFeatureFlags.www.js | 2 +- scripts/error-codes/codes.json | 3 ++- 6 files changed, 14 insertions(+), 23 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 80fc0ec6031..b418bac1f86 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -346,8 +346,7 @@ function useId(): string { const Dispatcher: DispatcherType = { getCacheForType, - // TODO: figure out why flow is complaining here - readContext: (readContext: any), + readContext, useCacheRefresh, useCallback, useContext, diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index b87b4b87732..97071a9b289 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -105,14 +105,3 @@ export function setCurrentCache(cache: Map | null) { export function getCurrentCache() { return currentCache; } - -type ServerContextCache = {[name: string]: ReactServerContext} | null; -let currentServerContexts: ServerContextCache = null; - -export function setCurrentServerContexts(contexts: ServerContextCache) { - currentServerContexts = contexts; -} - -export function getCurrentServerContexts(): ServerContextCache { - return currentServerContexts; -} diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index e292bbac0ab..630907e3960 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -39,12 +39,7 @@ import { } from './ReactFlightServerConfig'; import {getOrCreateServerContext} from './ReactServerContext'; -import { - Dispatcher, - getCurrentCache, - setCurrentCache, - setCurrentServerContexts, -} from './ReactFlightHooks'; +import {Dispatcher, getCurrentCache, setCurrentCache} from './ReactFlightHooks'; import { pushProvider, popProvider, @@ -872,6 +867,5 @@ function importServerContexts( registry[name] = context; } } - setCurrentServerContexts(registry); return getActiveContext(); } diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 1173337ce83..39b364b42f7 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -8,6 +8,8 @@ */ import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; +import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; import type { MutableSource, MutableSourceGetSnapshotFn, @@ -187,7 +189,13 @@ export function useMutableSource( export function useServerContext( Context: ReactServerContext, ): T { - // TODO: Warn if regular context is passed in + if (enableServerContext) { + if (Context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { + throw new Error( + 'useServerContext expects a context created with React.createServerContext', + ); + } + } const dispatcher = resolveDispatcher(); // $FlowFixMe This is unstable, thus optional return dispatcher.useServerContext(Context); diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 1012db58d69..44e3d1887f2 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -100,7 +100,7 @@ export const deletedTreeCleanUpLevel = 3; export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = true; -export const enableServerContext = true; +export const enableServerContext = false; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index b46f6ce6df4..8ed3b00458f 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -405,5 +405,6 @@ "417": "React currently only supports piping to one writable stream.", "418": "An error occurred during hydration. The server HTML was replaced with client content", "419": "useServerContext is only supported while rendering.", - "420": "ServerContext: %s already defined" + "420": "ServerContext: %s already defined", + "421": "useServerContext expects a context created with React.createServerContext" } From 0bb2d553b31bb54f734b1b4654ec460c88e44e75 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 17 Feb 2022 11:32:07 -0500 Subject: [PATCH 15/83] pass context in as array of arrays --- .../react-client/src/__tests__/ReactFlight-test.js | 14 ++------------ .../src/ReactNoopFlightServer.js | 2 +- packages/react-server/src/ReactFlightServer.js | 10 ++++------ 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 54fc5e5bb22..1cf6f8f4d4f 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -526,12 +526,7 @@ describe('ReactFlight', () => { return {React.useServerContext(ServerContext)}; } const transport = ReactNoopFlightServer.render(, { - context: [ - { - name: 'ServerContext', - value: 'Override', - }, - ], + context: [['ServerContext', 'Override']], }); act(() => { @@ -601,12 +596,7 @@ describe('ReactFlight', () => { } const transport = ReactNoopFlightServer.render(, { - context: [ - { - name: 'ServerContext', - value: 'Override', - }, - ], + context: [['ServerContext', 'Override']], }); expect(ClientContext).toBe(undefined); diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index ae09467430d..cb1f362f79b 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -64,7 +64,7 @@ type ServerContextJSONValue = type Options = { onError?: (error: mixed) => void, - context?: Array<{name: string, value: ServerContextJSONValue}>, + context?: Array<[string, ServerContextJSONValue]>, }; function render(model: ReactModel, options?: Options): Destination { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 630907e3960..e22a033983a 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -106,7 +106,7 @@ export type Request = { export type RequestOptions = { onError?: (error: mixed) => void, - context?: Array<{name: string, value: ServerContextJSONValue}>, + context?: Array<[string, ServerContextJSONValue]>, }; const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; @@ -154,7 +154,7 @@ export function createRequest( } function createRootContext( - reqContext?: Array<{name: string, value: ServerContextJSONValue}>, + reqContext?: Array<[string, ServerContextJSONValue]>, ) { return importServerContexts(reqContext); } @@ -854,14 +854,12 @@ export function startFlowing(request: Request, destination: Destination): void { } function importServerContexts( - contexts: - | Array<{name: string, value: ServerContextJSONValue}> - | typeof undefined, + contexts?: Array<[string, ServerContextJSONValue]>, ) { const registry: {[name: string]: ReactServerContext} = {}; if (contexts) { for (let i = 0; i < contexts.length; i++) { - const {name, value} = contexts[i]; + const [name, value] = contexts[i]; const context = getOrCreateServerContext(name, value); pushProvider(context, value); registry[name] = context; From 0dc363f0faff6ffa48cec48a45fda6d8c31cd5d9 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Tue, 22 Feb 2022 21:52:52 -0500 Subject: [PATCH 16/83] make importServerContext nott pollute the global context state --- packages/react-server/src/ReactFlightServer.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index e22a033983a..d41afca2b15 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -45,6 +45,7 @@ import { popProvider, switchContext, getActiveContext, + rootContextSnapshot, } from './ReactFlightNewContext'; import { @@ -856,6 +857,8 @@ export function startFlowing(request: Request, destination: Destination): void { function importServerContexts( contexts?: Array<[string, ServerContextJSONValue]>, ) { + const prevContext = getActiveContext(); + switchContext(rootContextSnapshot); const registry: {[name: string]: ReactServerContext} = {}; if (contexts) { for (let i = 0; i < contexts.length; i++) { @@ -865,5 +868,7 @@ function importServerContexts( registry[name] = context; } } - return getActiveContext(); + const importedContext = getActiveContext(); + switchContext(prevContext); + return importedContext; } From be811ec0c6e3185f772b8015fe6e2ac3da25ceb6 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Tue, 22 Feb 2022 21:58:13 -0500 Subject: [PATCH 17/83] merge main --- scripts/error-codes/codes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 26afab42633..7981c766f66 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -406,7 +406,7 @@ "418": "An error occurred during hydration. The server HTML was replaced with client content", "419": "useServerContext is only supported while rendering.", "420": "ServerContext: %s already defined", - "421": "useServerContext expects a context created with React.createServerContext" + "421": "useServerContext expects a context created with React.createServerContext", "422": "Hydration failed because the initial UI does not match what was rendered on the server.", "423": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", "424": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", From 3c8ec090a51c4582f6050fcadc438ab1a36cd3ad Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 23 Feb 2022 14:25:27 -0500 Subject: [PATCH 18/83] remove useServerContext --- .../react-client/src/ReactServerContext.js | 4 +- .../src/__tests__/ReactFlight-test.js | 16 +-- .../react-debug-tools/src/ReactDebugHooks.js | 13 --- .../src/server/ReactPartialRendererHooks.js | 15 --- .../src/ReactFiberHooks.new.js | 102 +----------------- .../src/ReactFiberHooks.old.js | 102 +----------------- .../src/ReactInternalTypes.js | 7 +- packages/react-server/src/ReactFizzHooks.js | 12 --- packages/react-server/src/ReactFlightHooks.js | 8 -- .../react-server/src/ReactServerContext.js | 4 +- .../src/ReactSuspenseTestUtils.js | 1 - packages/react/index.classic.fb.js | 1 - packages/react/index.experimental.js | 1 - packages/react/index.js | 1 - packages/react/index.modern.fb.js | 1 - packages/react/index.stable.js | 1 - packages/react/src/React.js | 2 - packages/react/src/ReactHooks.js | 19 ---- packages/react/src/ReactServerContext.js | 4 +- scripts/error-codes/codes.json | 16 ++- 20 files changed, 24 insertions(+), 306 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index caa3cb381c2..fdb80d1f316 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -72,7 +72,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } @@ -90,7 +90,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 1cf6f8f4d4f..1dc4f6f8db8 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -318,7 +318,7 @@ describe('ReactFlight', () => { 'hello from server', ); function Foo() { - const context = React.useServerContext(ServerContext); + const context = React.useContext(ServerContext); return
{context}
; } @@ -349,7 +349,7 @@ describe('ReactFlight', () => { ); } function Bar() { - const context = React.useServerContext(ServerContext); + const context = React.useContext(ServerContext); return context; } @@ -390,7 +390,7 @@ describe('ReactFlight', () => { ); } function Bar() { - const context = React.useServerContext(ServerContext); + const context = React.useContext(ServerContext); return {context}; } @@ -445,7 +445,7 @@ describe('ReactFlight', () => { throw promise; } Scheduler.unstable_yieldValue('rendered'); - const context = React.useServerContext(ServerContext); + const context = React.useContext(ServerContext); return context; } @@ -479,7 +479,7 @@ describe('ReactFlight', () => { function ClientBar() { Scheduler.unstable_yieldValue('ClientBar'); - const context = React.useServerContext(ServerContext); + const context = React.useContext(ServerContext); return {context}; } @@ -523,7 +523,7 @@ describe('ReactFlight', () => { 'default', ); function Bar() { - return {React.useServerContext(ServerContext)}; + return {React.useContext(ServerContext)}; } const transport = ReactNoopFlightServer.render(, { context: [['ServerContext', 'Override']], @@ -557,7 +557,7 @@ describe('ReactFlight', () => { function ClientBaz() { const context = inlineContextInitialization(); - const value = React.useServerContext(context); + const value = React.useContext(context); return
{value}
; } @@ -567,7 +567,7 @@ describe('ReactFlight', () => { return (
- {React.useServerContext(inlineLazyServerContextInitialization())} + {React.useContext(inlineLazyServerContextInitialization())}
diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index b418bac1f86..466d852fb69 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -14,7 +14,6 @@ import type { ReactContext, ReactServerContext, ReactProviderType, - ServerContextJSONValue, } from 'shared/ReactTypes'; import type { Fiber, @@ -121,17 +120,6 @@ function useContext(context: ReactContext): T { return context._currentValue; } -function useServerContext( - context: ReactServerContext, -): T { - hookLog.push({ - primitive: 'ServerContext', - stackError: new Error(), - value: context._currentValue, - }); - return context._currentValue; -} - function useState( initialState: (() => S) | S, ): [S, Dispatch>] { @@ -358,7 +346,6 @@ const Dispatcher: DispatcherType = { useMemo, useReducer, useRef, - useServerContext, useState, useTransition, useMutableSource, diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index b36cd289caa..1cd95f08fb6 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -14,8 +14,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, - ServerContextJSONValue, } from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; @@ -252,18 +250,6 @@ function useContext(context: ReactContext): T { return context[threadID]; } -function useServerContext( - context: ReactServerContext, -): T { - if (__DEV__) { - currentHookNameInDev = 'useContext'; - } - resolveCurrentlyRenderingComponent(); - const threadID = currentPartialRenderer.threadID; - validateContextBounds(context, threadID); - return context[threadID]; -} - function basicStateReducer(state: S, action: BasicStateAction): S { // $FlowFixMe: Flow doesn't like mixed types return typeof action === 'function' ? action(state) : action; @@ -547,7 +533,6 @@ export const Dispatcher: DispatcherType = { useReducer, useRef, useState, - useServerContext, useInsertionEffect, useLayoutEffect, useCallback, diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 6a1d7e089b9..c6f79e1fd3e 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -12,8 +12,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, - ServerContextJSONValue, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.new'; @@ -33,7 +31,6 @@ import { enableLazyContextPropagation, enableSuspenseLayoutEffectSemantics, enableUseMutableSource, - enableServerContext, } from 'shared/ReactFeatureFlags'; import { @@ -2382,28 +2379,6 @@ function getCacheForType(resourceType: () => T): T { return cacheForType; } -function mountServerContext( - context: ReactServerContext, -): T { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - mountHookTypesDev(); - return readContext(context); -} - -function updateServerContext( - context: ReactServerContext, -): T { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); -} - export const ContextOnlyDispatcher: Dispatcher = { readContext, @@ -2432,10 +2407,6 @@ if (enableCache) { (ContextOnlyDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError; } -if (enableServerContext) { - (ContextOnlyDispatcher: Dispatcher).useServerContext = throwInvalidHookError; -} - const HooksDispatcherOnMount: Dispatcher = { readContext, @@ -2463,10 +2434,6 @@ if (enableCache) { (HooksDispatcherOnMount: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnMount: Dispatcher).useCacheRefresh = mountRefresh; } -if (enableServerContext) { - (HooksDispatcherOnMount: Dispatcher).useServerContext = readContext; -} - const HooksDispatcherOnUpdate: Dispatcher = { readContext, @@ -2494,9 +2461,6 @@ if (enableCache) { (HooksDispatcherOnUpdate: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnUpdate: Dispatcher).useCacheRefresh = updateRefresh; } -if (enableServerContext) { - (HooksDispatcherOnUpdate: Dispatcher).useServerContext = readContext; -} const HooksDispatcherOnRerender: Dispatcher = { readContext, @@ -2525,9 +2489,6 @@ if (enableCache) { (HooksDispatcherOnRerender: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnRerender: Dispatcher).useCacheRefresh = updateRefresh; } -if (enableServerContext) { - (HooksDispatcherOnRerender: Dispatcher).useServerContext = readContext; -} let HooksDispatcherOnMountInDEV: Dispatcher | null = null; let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null; @@ -2703,9 +2664,6 @@ if (__DEV__) { return mountRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnMountInDEV: Dispatcher).useServerContext = mountServerContext; - } HooksDispatcherOnMountWithHookTypesInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2848,9 +2806,6 @@ if (__DEV__) { return mountRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useServerContext = updateServerContext; - } HooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2993,12 +2948,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = updateServerContext; - } - - if (!enableServerContext) { - } HooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3142,9 +3091,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = updateServerContext; - } InvalidNestedHooksDispatcherOnMountInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3300,24 +3246,8 @@ if (__DEV__) { (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).getCacheForType = getCacheForType; (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() { currentHookNameInDev = 'useCacheRefresh'; - updateHookTypesDev(); - return mountRefresh(); - }; - } - - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); mountHookTypesDev(); - return readContext(context); + return mountRefresh(); }; } @@ -3479,21 +3409,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }; - } InvalidNestedHooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3654,19 +3569,4 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }; - } } diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 450f27427dc..72c8111854c 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -12,8 +12,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, - ServerContextJSONValue, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.old'; @@ -33,7 +31,6 @@ import { enableLazyContextPropagation, enableSuspenseLayoutEffectSemantics, enableUseMutableSource, - enableServerContext, } from 'shared/ReactFeatureFlags'; import { @@ -2382,28 +2379,6 @@ function getCacheForType(resourceType: () => T): T { return cacheForType; } -function mountServerContext( - context: ReactServerContext, -): T { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - mountHookTypesDev(); - return readContext(context); -} - -function updateServerContext( - context: ReactServerContext, -): T { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); -} - export const ContextOnlyDispatcher: Dispatcher = { readContext, @@ -2432,10 +2407,6 @@ if (enableCache) { (ContextOnlyDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError; } -if (enableServerContext) { - (ContextOnlyDispatcher: Dispatcher).useServerContext = throwInvalidHookError; -} - const HooksDispatcherOnMount: Dispatcher = { readContext, @@ -2463,10 +2434,6 @@ if (enableCache) { (HooksDispatcherOnMount: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnMount: Dispatcher).useCacheRefresh = mountRefresh; } -if (enableServerContext) { - (HooksDispatcherOnMount: Dispatcher).useServerContext = readContext; -} - const HooksDispatcherOnUpdate: Dispatcher = { readContext, @@ -2494,9 +2461,6 @@ if (enableCache) { (HooksDispatcherOnUpdate: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnUpdate: Dispatcher).useCacheRefresh = updateRefresh; } -if (enableServerContext) { - (HooksDispatcherOnUpdate: Dispatcher).useServerContext = readContext; -} const HooksDispatcherOnRerender: Dispatcher = { readContext, @@ -2525,9 +2489,6 @@ if (enableCache) { (HooksDispatcherOnRerender: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnRerender: Dispatcher).useCacheRefresh = updateRefresh; } -if (enableServerContext) { - (HooksDispatcherOnRerender: Dispatcher).useServerContext = readContext; -} let HooksDispatcherOnMountInDEV: Dispatcher | null = null; let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null; @@ -2703,9 +2664,6 @@ if (__DEV__) { return mountRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnMountInDEV: Dispatcher).useServerContext = mountServerContext; - } HooksDispatcherOnMountWithHookTypesInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2848,9 +2806,6 @@ if (__DEV__) { return mountRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useServerContext = updateServerContext; - } HooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2993,12 +2948,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = updateServerContext; - } - - if (!enableServerContext) { - } HooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3142,9 +3091,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = updateServerContext; - } InvalidNestedHooksDispatcherOnMountInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3300,24 +3246,8 @@ if (__DEV__) { (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).getCacheForType = getCacheForType; (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() { currentHookNameInDev = 'useCacheRefresh'; - updateHookTypesDev(); - return mountRefresh(); - }; - } - - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); mountHookTypesDev(); - return readContext(context); + return mountRefresh(); }; } @@ -3479,21 +3409,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }; - } InvalidNestedHooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3654,19 +3569,4 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }; - } } diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index b5e11b015ca..83608591f36 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -12,7 +12,6 @@ import type { RefObject, ReactContext, ReactServerContext, - ServerContextJSONValue, MutableSourceSubscribeFn, MutableSourceGetSnapshotFn, MutableSourceVersion, @@ -46,8 +45,7 @@ export type HookType = | 'useMutableSource' | 'useSyncExternalStore' | 'useId' - | 'useCacheRefresh' - | 'useServerContext'; + | 'useCacheRefresh'; export type ContextDependency = { context: ReactContext | ReactServerContext, @@ -378,9 +376,6 @@ export type Dispatcher = {| getSnapshot: MutableSourceGetSnapshotFn, subscribe: MutableSourceSubscribeFn, ): Snapshot, - useServerContext?: ( - context: ReactServerContext, - ) => T, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index 1b04474a602..a3a79a93fbf 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -15,7 +15,6 @@ import type { MutableSourceSubscribeFn, ReactContext, ReactServerContext, - ServerContextJSONValue, } from 'shared/ReactTypes'; import type {ResponseState} from './ReactServerFormatConfig'; @@ -268,16 +267,6 @@ function useContext(context: ReactContext): T { return readContextImpl(context); } -function useServerContext( - context: ReactServerContext, -): T { - if (__DEV__) { - currentHookNameInDev = 'useServerContext'; - } - resolveCurrentlyRenderingComponent(); - return readContextImpl(context); -} - function basicStateReducer(state: S, action: BasicStateAction): S { // $FlowFixMe: Flow doesn't like mixed types return typeof action === 'function' ? action(state) : action; @@ -552,7 +541,6 @@ function noop(): void {} export const Dispatcher: DispatcherType = { readContext, useContext, - useServerContext, useMemo, useReducer, useRef, diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index 97071a9b289..bd703b357f8 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -69,14 +69,6 @@ export const Dispatcher: DispatcherType = { useEffect: (unsupportedHook: any), useId: (unsupportedHook: any), useMutableSource: (unsupportedHook: any), - useServerContext: function useServerContext( - context: ReactServerContext, - ): T { - if (!currentCache) { - throw new Error('useServerContext is only supported while rendering.'); - } - return readContextImpl(context); - }, useSyncExternalStore: (unsupportedHook: any), useCacheRefresh(): (?() => T, ?T) => void { return unsupportedRefresh; diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index caa3cb381c2..fdb80d1f316 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -72,7 +72,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } @@ -90,7 +90,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } diff --git a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js index 27115bf6b4b..d75f1f7c9b4 100644 --- a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js +++ b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js @@ -46,7 +46,6 @@ export function waitForSuspense(fn: () => T): Promise { useMutableSource: unsupported, useSyncExternalStore: unsupported, useCacheRefresh: unsupported, - useServerContext: unsupported, }; // Not using async/await because we don't compile it. return new Promise((resolve, reject) => { diff --git a/packages/react/index.classic.fb.js b/packages/react/index.classic.fb.js index 122f679fcfd..76326a0fe59 100644 --- a/packages/react/index.classic.fb.js +++ b/packages/react/index.classic.fb.js @@ -56,7 +56,6 @@ export { useMutableSource as unstable_useMutableSource, useReducer, useRef, - useServerContext, useState, useSyncExternalStore, useTransition, diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js index 1ee0c52731f..19af075993d 100644 --- a/packages/react/index.experimental.js +++ b/packages/react/index.experimental.js @@ -49,7 +49,6 @@ export { useMemo, useReducer, useRef, - useServerContext, useState, useSyncExternalStore, useTransition, diff --git a/packages/react/index.js b/packages/react/index.js index aa25252e253..084aabb53c6 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -74,7 +74,6 @@ export { useLayoutEffect, useMemo, useMutableSource, - useServerContext, useSyncExternalStore, useReducer, useRef, diff --git a/packages/react/index.modern.fb.js b/packages/react/index.modern.fb.js index 231f094922f..e9f80ade061 100644 --- a/packages/react/index.modern.fb.js +++ b/packages/react/index.modern.fb.js @@ -55,7 +55,6 @@ export { useMutableSource as unstable_useMutableSource, useReducer, useRef, - useServerContext, useState, useSyncExternalStore, useTransition, diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js index e91878c4d8a..5fe9b51a56c 100644 --- a/packages/react/index.stable.js +++ b/packages/react/index.stable.js @@ -41,7 +41,6 @@ export { useMemo, useReducer, useRef, - useServerContext, useState, useSyncExternalStore, useTransition, diff --git a/packages/react/src/React.js b/packages/react/src/React.js index a64384347a3..d13bf62941b 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -51,7 +51,6 @@ import { useSyncExternalStore, useReducer, useRef, - useServerContext, useState, useTransition, useDeferredValue, @@ -101,7 +100,6 @@ export { useLayoutEffect, useMemo, useMutableSource, - useServerContext, useSyncExternalStore, useReducer, useRef, diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 39b364b42f7..12a5350083f 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -8,15 +8,11 @@ */ import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes'; -import {enableServerContext} from 'shared/ReactFeatureFlags'; -import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; import type { MutableSource, MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, - ServerContextJSONValue, } from 'shared/ReactTypes'; import ReactCurrentDispatcher from './ReactCurrentDispatcher'; @@ -186,21 +182,6 @@ export function useMutableSource( return dispatcher.useMutableSource(source, getSnapshot, subscribe); } -export function useServerContext( - Context: ReactServerContext, -): T { - if (enableServerContext) { - if (Context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { - throw new Error( - 'useServerContext expects a context created with React.createServerContext', - ); - } - } - const dispatcher = resolveDispatcher(); - // $FlowFixMe This is unstable, thus optional - return dispatcher.useServerContext(Context); -} - export function useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index caa3cb381c2..fdb80d1f316 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -72,7 +72,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } @@ -90,7 +90,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 7981c766f66..af8d9ad78ca 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -404,13 +404,11 @@ "416": "This environment don't support binary chunks.", "417": "React currently only supports piping to one writable stream.", "418": "An error occurred during hydration. The server HTML was replaced with client content", - "419": "useServerContext is only supported while rendering.", - "420": "ServerContext: %s already defined", - "421": "useServerContext expects a context created with React.createServerContext", - "422": "Hydration failed because the initial UI does not match what was rendered on the server.", - "423": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", - "424": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", - "425": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", - "426": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", - "427": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering." + "419": "ServerContext: %s already defined", + "420": "Hydration failed because the initial UI does not match what was rendered on the server.", + "421": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", + "422": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", + "423": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", + "424": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", + "425": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering." } From 9c7e061e1ef40cc142f9e9eab4d1054ded7ef45f Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 24 Feb 2022 11:29:24 -0500 Subject: [PATCH 19/83] dont rely on object getters in ReactServerContext and disallow JSX --- .../react-client/src/ReactServerContext.js | 56 ++------------ .../src/__tests__/ReactFlight-test.js | 43 ++++++++--- .../src/__tests__/ReactDOMFizzServer-test.js | 54 ++++++++++++++ .../src/server/ReactPartialRendererHooks.js | 1 + .../src/ReactFiberHooks.new.js | 1 + .../src/ReactFiberHooks.old.js | 1 + .../src/ReactFiberNewContext.new.js | 13 +++- .../src/ReactFiberNewContext.old.js | 13 +++- .../react-server/src/ReactFizzNewContext.js | 19 ++++- packages/react-server/src/ReactFlightHooks.js | 2 +- .../react-server/src/ReactFlightServer.js | 36 ++++++++- .../react-server/src/ReactServerContext.js | 66 +++++------------ packages/react/src/ReactServerContext.js | 74 +++++-------------- .../react/src/ReactServerContextRegistry.js | 4 +- packages/shared/ReactSymbols.js | 5 ++ packages/shared/ReactTypes.js | 2 - .../shared/forks/ReactFeatureFlags.www.js | 2 +- scripts/error-codes/codes.json | 4 +- scripts/jest/matchers/toWarnDev.js | 4 +- 19 files changed, 221 insertions(+), 179 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index fdb80d1f316..0925d1f7c53 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -10,6 +10,7 @@ import { REACT_PROVIDER_TYPE, REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, } from 'shared/ReactSymbols'; import type { @@ -24,11 +25,6 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -let DEFAULT_PLACEHOLDER; -if (enableServerContext) { - DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; -} - export function createServerContext( globalName: string, defaultValue: T, @@ -43,8 +39,7 @@ export function createServerContext( ); } const context = globalServerContextRegistry[globalName]; - if (!context._definitionLoaded) { - context._definitionLoaded = true; + if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { context._defaultValue = defaultValue; } else { throw new Error(`ServerContext: ${globalName} already defined`); @@ -63,47 +58,10 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - __currentValue: defaultValue, - __currentValue2: defaultValue, - - get _currentValue() { - const value = context.__currentValue; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - } - return value; - }, - - set _currentValue(value) { - context.__currentValue = value; - }, - - get _currentValue2() { - const value = context.__currentValue2; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - return (undefined: any); - } - return value; - }, - - set _currentValue2(value) { - context.__currentValue2 = value; - }, + _currentValue: defaultValue, + _currentValue2: defaultValue, - _defaultValue: (undefined: any), + _defaultValue: defaultValue, // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. @@ -132,11 +90,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string, value: any) { +export function getOrCreateServerContext(globalName: string) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - value === undefined ? DEFAULT_PLACEHOLDER : value, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 1dc4f6f8db8..d18e10ba6cf 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -363,6 +363,38 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); }); + // @gate enableServerContext + it('errors if you try passing JSX through ServerContext value', () => { + const ServerContext = React.createServerContext('ServerContext', { + foo: { + bar: hi this is default, + }, + }); + + function Foo() { + return ( +
+ hi this is server, + }, + }}> + + +
+ ); + } + function Bar() { + const context = React.useContext(ServerContext); + return context.foo.bar; + } + + expect(() => { + ReactNoopFlightServer.render(); + }).toErrorDev('React elements are not allowed in ServerContext'); + }); + // @gate enableServerContext it('propagates ServerContext and cleansup providers in flight', () => { const ServerContext = React.createServerContext( @@ -595,9 +627,7 @@ describe('ReactFlight', () => { ); } - const transport = ReactNoopFlightServer.render(, { - context: [['ServerContext', 'Override']], - }); + const transport = ReactNoopFlightServer.render(); expect(ClientContext).toBe(undefined); act(() => { @@ -615,12 +645,7 @@ describe('ReactFlight', () => {
test
-
Override
- - {/** In practice this would also be Override because the */} - {/** server context sent up to the server would be around this*/} - {/** tree. For this test we didn't do that though so it uses the */} - {/** real default */} +
default
default
default
diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 01397d00004..fb3fb7bf645 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -2386,4 +2386,58 @@ describe('ReactDOMFizzServer', () => { 'Suspense boundary. Switched to client rendering.', ]); }); + + // @gate enableServerContext + it('supports ServerContext', async () => { + const {getOrCreateServerContext} = require('react/src/ReactServerContext'); + const ServerContext = getOrCreateServerContext('ServerContext'); + + let initialized = false; + function inlineLazyServerContextInitialization() { + if (!initialized) { + initialized = true; + React.createServerContext('ServerContext', 'default'); + } + } + + function Foo() { + return ( + <> + + + + + + + + + + + + + + + ); + } + function Bar() { + const context = React.useContext(ServerContext); + inlineLazyServerContextInitialization(); + return {context}; + } + + await act(async () => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + const {pipe} = ReactDOMFizzServer.renderToPipeableStream(); + pipe(writable); + }); + + expect(getVisibleChildren(container)).toEqual([ + hi this is server, + hi this is server2, + hi this is server outer, + hi this is server outer2, + default, + ]); + }); }); diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 1cd95f08fb6..e2280fbd48b 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -14,6 +14,7 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, } from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index c6f79e1fd3e..745593243d9 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -12,6 +12,7 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.new'; diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 72c8111854c..48dceb85a1d 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -12,6 +12,7 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.old'; diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 480e86dbfd6..9a516a3fec2 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -49,6 +49,7 @@ import { enableSuspenseServerRenderer, enableLazyContextPropagation, } from 'shared/ReactFeatureFlags'; +import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; const valueCursor: StackCursor = createCursor(null); @@ -136,9 +137,17 @@ export function popProvider( const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = currentValue; + if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + context._currentValue = ((context: any): ReactServerContext)._defaultValue; + } else { + context._currentValue = currentValue; + } } else { - context._currentValue2 = currentValue; + if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + context._currentValue2 = ((context: any): ReactServerContext)._defaultValue; + } else { + context._currentValue2 = currentValue; + } } } diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 78cca5e23b4..2a990c9b040 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -49,6 +49,7 @@ import { enableSuspenseServerRenderer, enableLazyContextPropagation, } from 'shared/ReactFeatureFlags'; +import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; const valueCursor: StackCursor = createCursor(null); @@ -136,9 +137,17 @@ export function popProvider( const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = currentValue; + if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + context._currentValue = ((context: any): ReactServerContext)._defaultValue; + } else { + context._currentValue = currentValue; + } } else { - context._currentValue2 = currentValue; + if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + context._currentValue2 = ((context: any): ReactServerContext)._defaultValue; + } else { + context._currentValue2 = currentValue; + } } } diff --git a/packages/react-server/src/ReactFizzNewContext.js b/packages/react-server/src/ReactFizzNewContext.js index 01df3d84494..c333b5681de 100644 --- a/packages/react-server/src/ReactFizzNewContext.js +++ b/packages/react-server/src/ReactFizzNewContext.js @@ -7,6 +7,7 @@ * @flow */ +import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; import type {ReactContext, ReactServerContext} from 'shared/ReactTypes'; import {isPrimaryRenderer} from './ReactServerFormatConfig'; @@ -246,7 +247,14 @@ export function popProvider( } } if (isPrimaryRenderer) { - prevSnapshot.context._currentValue = prevSnapshot.parentValue; + const value = prevSnapshot.parentValue; + if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + prevSnapshot.context._currentValue = + // $FlowExpectedError - Effectively refined context to ServerContext + (prevSnapshot.context: ReactServerContext)._defaultValue; + } else { + prevSnapshot.context._currentValue = value; + } if (__DEV__) { if ( context._currentRenderer !== undefined && @@ -261,7 +269,14 @@ export function popProvider( context._currentRenderer = rendererSigil; } } else { - prevSnapshot.context._currentValue2 = prevSnapshot.parentValue; + const value = prevSnapshot.parentValue; + if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + prevSnapshot.context._currentValue2 = + // $FlowExpectedError - Effectively refined context to ServerContext + (prevSnapshot.context: ReactServerContext)._defaultValue; + } else { + prevSnapshot.context._currentValue2 = value; + } if (__DEV__) { if ( context._currentRenderer2 !== undefined && diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index bd703b357f8..0b12e00bfa3 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -59,7 +59,7 @@ export const Dispatcher: DispatcherType = { return entry; }, readContext, - useContext: (unsupportedHook: any), + useContext: (readContext: any), useReducer: (unsupportedHook: any), useRef: (unsupportedHook: any), useState: (unsupportedHook: any), diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index d41afca2b15..b1de0395784 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -206,6 +206,20 @@ function attemptResolveElement( } case REACT_PROVIDER_TYPE: { pushProvider(type._context, props.value); + if (__DEV__) { + const extraKeys = Object.keys(props).filter(value => { + if (value === 'children' || value === 'value') { + return false; + } + return true; + }); + if (extraKeys.length !== 0) { + throw new Error( + 'ServerContext can only have a value prop and children. Found: ' + + JSON.stringify(extraKeys), + ); + } + } return [ REACT_PROVIDER_TYPE, type._context.displayName, @@ -415,6 +429,9 @@ function isReactElement(value: mixed) { ); } +let insideContextProps = null; +let isInsideContextValue = false; + export function resolveModelToJSON( request: Request, parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray, @@ -448,6 +465,19 @@ export function resolveModelToJSON( ); } + if (__DEV__) { + if (parent[0] === REACT_PROVIDER_TYPE && key === '3') { + insideContextProps = value; + } else if (insideContextProps === parent && key === 'value') { + isInsideContextValue = true; + } else if (insideContextProps === parent && key === 'children') { + isInsideContextValue = false; + } + if (isReactElement(value) && isInsideContextValue) { + throw new Error('React elements are not allowed in ServerContext'); + } + } + // Resolve server components. while (isReactElement(value)) { // TODO: Concatenate keys of parents onto children. @@ -491,6 +521,10 @@ export function resolveModelToJSON( parent[0] === REACT_PROVIDER_TYPE ) { popProvider((value: any)); + if (__DEV__) { + insideContextProps = null; + isInsideContextValue = false; + } return (undefined: any); } @@ -863,7 +897,7 @@ function importServerContexts( if (contexts) { for (let i = 0; i < contexts.length; i++) { const [name, value] = contexts[i]; - const context = getOrCreateServerContext(name, value); + const context = getOrCreateServerContext(name); pushProvider(context, value); registry[name] = context; } diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index fdb80d1f316..790e9485f6d 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -10,6 +10,7 @@ import { REACT_PROVIDER_TYPE, REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, } from 'shared/ReactSymbols'; import type { @@ -24,11 +25,6 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -let DEFAULT_PLACEHOLDER; -if (enableServerContext) { - DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; -} - export function createServerContext( globalName: string, defaultValue: T, @@ -43,9 +39,18 @@ export function createServerContext( ); } const context = globalServerContextRegistry[globalName]; - if (!context._definitionLoaded) { - context._definitionLoaded = true; + if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { context._defaultValue = defaultValue; + if ( + context._currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { + context._currentValue = defaultValue; + } + if ( + context._currentValue2 === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { + context._currentValue2 = defaultValue; + } } else { throw new Error(`ServerContext: ${globalName} already defined`); } @@ -63,47 +68,10 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - __currentValue: defaultValue, - __currentValue2: defaultValue, - - get _currentValue() { - const value = context.__currentValue; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - } - return value; - }, - - set _currentValue(value) { - context.__currentValue = value; - }, - - get _currentValue2() { - const value = context.__currentValue2; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - return (undefined: any); - } - return value; - }, - - set _currentValue2(value) { - context.__currentValue2 = value; - }, + _currentValue: defaultValue, + _currentValue2: defaultValue, - _defaultValue: (undefined: any), + _defaultValue: defaultValue, // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. @@ -132,11 +100,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string, value: any) { +export function getOrCreateServerContext(globalName: string) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - value === undefined ? DEFAULT_PLACEHOLDER : value, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index fdb80d1f316..ce2730e73fa 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -10,6 +10,7 @@ import { REACT_PROVIDER_TYPE, REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, } from 'shared/ReactSymbols'; import type { @@ -24,11 +25,6 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -let DEFAULT_PLACEHOLDER; -if (enableServerContext) { - DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; -} - export function createServerContext( globalName: string, defaultValue: T, @@ -36,19 +32,22 @@ export function createServerContext( if (!enableServerContext) { throw new Error('Not implemented.'); } - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + let context; + if (globalServerContextRegistry[globalName]) { + context = globalServerContextRegistry[globalName]; + if ( + context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { + context._defaultValue = defaultValue; + } else { + throw new Error(`ServerContext: ${globalName} already defined`); + } + } else { + context = globalServerContextRegistry[globalName] = _createServerContext( globalName, defaultValue, ); } - const context = globalServerContextRegistry[globalName]; - if (!context._definitionLoaded) { - context._definitionLoaded = true; - context._defaultValue = defaultValue; - } else { - throw new Error(`ServerContext: ${globalName} already defined`); - } return context; } @@ -63,47 +62,10 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - __currentValue: defaultValue, - __currentValue2: defaultValue, - - get _currentValue() { - const value = context.__currentValue; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - } - return value; - }, - - set _currentValue(value) { - context.__currentValue = value; - }, - - get _currentValue2() { - const value = context.__currentValue2; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - return (undefined: any); - } - return value; - }, - - set _currentValue2(value) { - context.__currentValue2 = value; - }, + _currentValue: defaultValue, + _currentValue2: defaultValue, - _defaultValue: (undefined: any), + _defaultValue: defaultValue, // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. @@ -132,11 +94,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string, value: any) { +export function getOrCreateServerContext(globalName: string) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - value === undefined ? DEFAULT_PLACEHOLDER : value, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react/src/ReactServerContextRegistry.js b/packages/react/src/ReactServerContextRegistry.js index f24d9474309..c98f12b6e35 100644 --- a/packages/react/src/ReactServerContextRegistry.js +++ b/packages/react/src/ReactServerContextRegistry.js @@ -2,6 +2,4 @@ import type {ReactServerContext} from 'shared/ReactTypes'; export const globalServerContextRegistry: { [globalName: string]: ReactServerContext, -} = { - __defaultValue: {}, -}; +} = {}; diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index 85b382a7694..1939ff4bf30 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -32,6 +32,8 @@ export let REACT_OFFSCREEN_TYPE = 0xeae2; export let REACT_LEGACY_HIDDEN_TYPE = 0xeae3; export let REACT_CACHE_TYPE = 0xeae4; export let REACT_TRACING_MARKER_TYPE = 0xeae5; +export let REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = + '!__$$defaultValueNotLoaded'; if (typeof Symbol === 'function' && Symbol.for) { const symbolFor = Symbol.for; @@ -54,6 +56,9 @@ if (typeof Symbol === 'function' && Symbol.for) { REACT_LEGACY_HIDDEN_TYPE = symbolFor('react.legacy_hidden'); REACT_CACHE_TYPE = symbolFor('react.cache'); REACT_TRACING_MARKER_TYPE = symbolFor('react.tracing_marker'); + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = symbolFor( + 'react.server_context.defaultValue', + ); } const MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator; diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 895d86d5e30..d939b7e05fb 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -88,8 +88,6 @@ export type ReactServerContext = { $$typeof: Symbol | number, Provider: ReactServerProviderType, _defaultValue: T, - __currentValue: T, - __currentValue2: T, _currentValue: T, _currentValue2: T, _currentRenderer?: Object | null, diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index b107b54d9fb..a34561f2c62 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -103,7 +103,7 @@ export const deletedTreeCleanUpLevel = 3; export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = true; -export const enableServerContext = false; +export const enableServerContext = true; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index af8d9ad78ca..aef64d5e58b 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -410,5 +410,7 @@ "422": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", "423": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", "424": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", - "425": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering." + "425": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", + "426": "ServerContext can only have a value prop and children. Found: %s", + "427": "React elements are not allowed in ServerContext" } diff --git a/scripts/jest/matchers/toWarnDev.js b/scripts/jest/matchers/toWarnDev.js index 5e2a144b4e2..dd395d30d11 100644 --- a/scripts/jest/matchers/toWarnDev.js +++ b/scripts/jest/matchers/toWarnDev.js @@ -86,7 +86,9 @@ const createMatcherFor = (consoleMethod, matcherName) => // doesn't match the number of arguments. // We'll fail the test if it happens. let argIndex = 0; - format.replace(/%s/g, () => argIndex++); + if (format.replace) { + format.replace(/%s/g, () => argIndex++); + } if (argIndex !== args.length) { lastWarningWithMismatchingFormat = { format, From 6f71944f14a9a0be2a2a163aba79d98985a10ecc Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 24 Feb 2022 11:37:00 -0500 Subject: [PATCH 20/83] add symbols to devtools + rename globalServerContextRegistry to just ContextRegistry --- packages/react-client/src/ReactServerContext.js | 17 ++++++++--------- .../src/__tests__/ReactFlight-test.js | 7 +++---- .../src/backend/ReactSymbols.js | 7 +++++++ .../src/backend/renderer.js | 4 ++++ packages/react-server/src/ReactServerContext.js | 17 ++++++++--------- packages/react/src/ReactServerContext.js | 17 ++++++++--------- .../react/src/ReactServerContextRegistry.js | 2 +- packages/react/src/ReactSharedInternals.js | 4 ++-- .../react/src/forks/ReactSharedInternals.umd.js | 4 ++-- packages/shared/ReactSymbols.js | 3 +-- 10 files changed, 44 insertions(+), 38 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index 0925d1f7c53..61af8e3ec31 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -22,8 +22,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals'; import {enableServerContext} from 'shared/ReactFeatureFlags'; -const globalServerContextRegistry = - ReactSharedInternals.globalServerContextRegistry; +const ContextRegistry = ReactSharedInternals.ContextRegistry; export function createServerContext( globalName: string, @@ -32,13 +31,13 @@ export function createServerContext( if (!enableServerContext) { throw new Error('Not implemented.'); } - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = _createServerContext( globalName, defaultValue, ); } - const context = globalServerContextRegistry[globalName]; + const context = ContextRegistry[globalName]; if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { context._defaultValue = defaultValue; } else { @@ -81,7 +80,7 @@ function _createServerContext( context._currentRenderer = null; context._currentRenderer2 = null; } - globalServerContextRegistry[globalName] = context; + ContextRegistry[globalName] = context; return context; } @@ -91,11 +90,11 @@ function _createServerContext( // if thats the case and when the definition loads it will set the correct // default value. export function getOrCreateServerContext(globalName: string) { - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = _createServerContext( globalName, REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } - return globalServerContextRegistry[globalName]; + return ContextRegistry[globalName]; } diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index d18e10ba6cf..b4d1eaa1621 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -18,7 +18,7 @@ let ReactNoopFlightClient; let ErrorBoundary; let NoErrorExpected; let Scheduler; -let globalServerContextRegistry; +let ContextRegistry; describe('ReactFlight', () => { beforeEach(() => { @@ -32,8 +32,7 @@ describe('ReactFlight', () => { Scheduler = require('scheduler'); const ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - globalServerContextRegistry = - ReactSharedInternals.globalServerContextRegistry; + ContextRegistry = ReactSharedInternals.ContextRegistry; ErrorBoundary = class extends React.Component { state = {hasError: false, error: null}; @@ -631,7 +630,7 @@ describe('ReactFlight', () => { expect(ClientContext).toBe(undefined); act(() => { - delete globalServerContextRegistry.ServerContext; + delete ContextRegistry.ServerContext; ServerContext._currentRenderer = null; ServerContext._currentRenderer2 = null; const serverModel = ReactNoopFlightClient.read(transport); diff --git a/packages/react-devtools-shared/src/backend/ReactSymbols.js b/packages/react-devtools-shared/src/backend/ReactSymbols.js index ebc6920be8d..cbeb1b9c918 100644 --- a/packages/react-devtools-shared/src/backend/ReactSymbols.js +++ b/packages/react-devtools-shared/src/backend/ReactSymbols.js @@ -19,6 +19,9 @@ export const CONCURRENT_MODE_SYMBOL_STRING = 'Symbol(react.concurrent_mode)'; export const CONTEXT_NUMBER = 0xeace; export const CONTEXT_SYMBOL_STRING = 'Symbol(react.context)'; +export const SERVER_CONTEXT_NUMBER = 0xeacf; +export const SERVER_CONTEXT_SYMBOL_STRING = 'Symbol(react.server_context)'; + export const DEPRECATED_ASYNC_MODE_SYMBOL_STRING = 'Symbol(react.async_mode)'; export const ELEMENT_NUMBER = 0xeac7; @@ -60,3 +63,7 @@ export const SUSPENSE_SYMBOL_STRING = 'Symbol(react.suspense)'; export const SUSPENSE_LIST_NUMBER = 0xead8; export const SUSPENSE_LIST_SYMBOL_STRING = 'Symbol(react.suspense_list)'; + +export const SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED_NUMBER = 0xeae6; +export const SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED_SYMBOL_STRING = + 'Symbol(react.server_context.defaultValue)'; diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js index 88c1d536083..70225e8281c 100644 --- a/packages/react-devtools-shared/src/backend/renderer.js +++ b/packages/react-devtools-shared/src/backend/renderer.js @@ -85,6 +85,8 @@ import { FORWARD_REF_SYMBOL_STRING, MEMO_NUMBER, MEMO_SYMBOL_STRING, + SERVER_CONTEXT_NUMBER, + SERVER_CONTEXT_SYMBOL_STRING, } from './ReactSymbols'; import {format} from './utils'; import { @@ -511,6 +513,8 @@ export function getInternalReactConstants( return `${resolvedContext.displayName || 'Context'}.Provider`; case CONTEXT_NUMBER: case CONTEXT_SYMBOL_STRING: + case SERVER_CONTEXT_NUMBER: + case SERVER_CONTEXT_SYMBOL_STRING: // 16.3-16.5 read from "type" because the Consumer is the actual context object. // 16.6+ should read from "type._context" because Consumer can be different (in DEV). // NOTE Keep in sync with inspectElementRaw() diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index 790e9485f6d..eacfbdf0640 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -22,8 +22,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals'; import {enableServerContext} from 'shared/ReactFeatureFlags'; -const globalServerContextRegistry = - ReactSharedInternals.globalServerContextRegistry; +const ContextRegistry = ReactSharedInternals.ContextRegistry; export function createServerContext( globalName: string, @@ -32,13 +31,13 @@ export function createServerContext( if (!enableServerContext) { throw new Error('Not implemented.'); } - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = _createServerContext( globalName, defaultValue, ); } - const context = globalServerContextRegistry[globalName]; + const context = ContextRegistry[globalName]; if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { context._defaultValue = defaultValue; if ( @@ -91,7 +90,7 @@ function _createServerContext( context._currentRenderer = null; context._currentRenderer2 = null; } - globalServerContextRegistry[globalName] = context; + ContextRegistry[globalName] = context; return context; } @@ -101,11 +100,11 @@ function _createServerContext( // if thats the case and when the definition loads it will set the correct // default value. export function getOrCreateServerContext(globalName: string) { - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = _createServerContext( globalName, REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } - return globalServerContextRegistry[globalName]; + return ContextRegistry[globalName]; } diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index ce2730e73fa..a236de4b2a4 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -22,8 +22,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals'; import {enableServerContext} from 'shared/ReactFeatureFlags'; -const globalServerContextRegistry = - ReactSharedInternals.globalServerContextRegistry; +const ContextRegistry = ReactSharedInternals.ContextRegistry; export function createServerContext( globalName: string, @@ -33,8 +32,8 @@ export function createServerContext( throw new Error('Not implemented.'); } let context; - if (globalServerContextRegistry[globalName]) { - context = globalServerContextRegistry[globalName]; + if (ContextRegistry[globalName]) { + context = ContextRegistry[globalName]; if ( context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED ) { @@ -43,7 +42,7 @@ export function createServerContext( throw new Error(`ServerContext: ${globalName} already defined`); } } else { - context = globalServerContextRegistry[globalName] = _createServerContext( + context = ContextRegistry[globalName] = _createServerContext( globalName, defaultValue, ); @@ -85,7 +84,7 @@ function _createServerContext( context._currentRenderer = null; context._currentRenderer2 = null; } - globalServerContextRegistry[globalName] = context; + ContextRegistry[globalName] = context; return context; } @@ -95,11 +94,11 @@ function _createServerContext( // if thats the case and when the definition loads it will set the correct // default value. export function getOrCreateServerContext(globalName: string) { - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = _createServerContext( globalName, REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } - return globalServerContextRegistry[globalName]; + return ContextRegistry[globalName]; } diff --git a/packages/react/src/ReactServerContextRegistry.js b/packages/react/src/ReactServerContextRegistry.js index c98f12b6e35..dda738ac743 100644 --- a/packages/react/src/ReactServerContextRegistry.js +++ b/packages/react/src/ReactServerContextRegistry.js @@ -1,5 +1,5 @@ import type {ReactServerContext} from 'shared/ReactTypes'; -export const globalServerContextRegistry: { +export const ContextRegistry: { [globalName: string]: ReactServerContext, } = {}; diff --git a/packages/react/src/ReactSharedInternals.js b/packages/react/src/ReactSharedInternals.js index de3cc15e724..d7d7d07c86b 100644 --- a/packages/react/src/ReactSharedInternals.js +++ b/packages/react/src/ReactSharedInternals.js @@ -12,7 +12,7 @@ import ReactCurrentActQueue from './ReactCurrentActQueue'; import ReactCurrentOwner from './ReactCurrentOwner'; import ReactDebugCurrentFrame from './ReactDebugCurrentFrame'; import {enableServerContext} from 'shared/ReactFeatureFlags'; -import {globalServerContextRegistry} from './ReactServerContextRegistry'; +import {ContextRegistry} from './ReactServerContextRegistry'; const ReactSharedInternals = { ReactCurrentDispatcher, @@ -28,7 +28,7 @@ if (__DEV__) { } if (enableServerContext) { - ReactSharedInternals.globalServerContextRegistry = globalServerContextRegistry; + ReactSharedInternals.ContextRegistry = ContextRegistry; } export default ReactSharedInternals; diff --git a/packages/react/src/forks/ReactSharedInternals.umd.js b/packages/react/src/forks/ReactSharedInternals.umd.js index 87dd07bd0a7..ccf0b159843 100644 --- a/packages/react/src/forks/ReactSharedInternals.umd.js +++ b/packages/react/src/forks/ReactSharedInternals.umd.js @@ -13,7 +13,7 @@ import ReactCurrentOwner from '../ReactCurrentOwner'; import ReactDebugCurrentFrame from '../ReactDebugCurrentFrame'; import ReactCurrentBatchConfig from '../ReactCurrentBatchConfig'; import {enableServerContext} from 'shared/ReactFeatureFlags'; -import {globalServerContextRegistry} from '../ReactServerContextRegistry'; +import {ContextRegistry} from '../ReactServerContextRegistry'; const ReactSharedInternals = { ReactCurrentDispatcher, @@ -36,7 +36,7 @@ if (__DEV__) { } if (enableServerContext) { - ReactSharedInternals.globalServerContextRegistry = globalServerContextRegistry; + ReactSharedInternals.ContextRegistry = ContextRegistry; } export default ReactSharedInternals; diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index 1939ff4bf30..591a1189914 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -32,8 +32,7 @@ export let REACT_OFFSCREEN_TYPE = 0xeae2; export let REACT_LEGACY_HIDDEN_TYPE = 0xeae3; export let REACT_CACHE_TYPE = 0xeae4; export let REACT_TRACING_MARKER_TYPE = 0xeae5; -export let REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = - '!__$$defaultValueNotLoaded'; +export let REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = 0xeae6; if (typeof Symbol === 'function' && Symbol.for) { const symbolFor = Symbol.for; From d74b7bdfd344f18bb03f712727a3dfc7fdb1475d Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 24 Feb 2022 11:50:57 -0500 Subject: [PATCH 21/83] gate test case as experimental --- packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index fb3fb7bf645..ddcf5759be4 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -2387,7 +2387,7 @@ describe('ReactDOMFizzServer', () => { ]); }); - // @gate enableServerContext + // @gate enableServerContext && experimental it('supports ServerContext', async () => { const {getOrCreateServerContext} = require('react/src/ReactServerContext'); const ServerContext = getOrCreateServerContext('ServerContext'); From 4d9d014ecbb5115c82e20492fa781114611bc28b Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 13:34:53 -0500 Subject: [PATCH 22/83] feedback --- .../react-client/src/ReactFlightClient.js | 9 +- .../react-client/src/ReactServerContext.js | 100 ---------------- .../src/__tests__/ReactFlight-test.js | 10 +- .../react-debug-tools/src/ReactDebugHooks.js | 16 +-- .../src/__tests__/ReactDOMFizzServer-test.js | 18 ++- .../src/server/ReactPartialRenderer.js | 11 +- .../src/server/ReactPartialRendererContext.js | 2 +- .../src/ReactNoopFlightServer.js | 8 +- .../src/ReactFiberBeginWork.new.js | 3 +- .../src/ReactFiberBeginWork.old.js | 3 +- .../src/ReactFiberNewContext.new.js | 5 +- .../src/ReactFiberNewContext.old.js | 5 +- .../src/ReactFiberWorkLoop.new.js | 1 + .../src/getComponentNameFromFiber.js | 2 +- .../src/ReactFlightDOMServerBrowser.js | 15 ++- .../src/ReactFlightDOMServerNode.js | 15 ++- .../react-server/src/ReactFlightServer.js | 33 +++--- .../react-server/src/ReactServerContext.js | 110 ------------------ packages/react/src/React.js | 2 +- packages/react/src/ReactServerContext.js | 104 ----------------- packages/shared/ReactServerContext.js | 92 +++++++++++++++ packages/shared/ReactServerContextRegistry.js | 24 ++++ packages/shared/ReactTypes.js | 17 +-- packages/shared/getComponentNameFromType.js | 2 +- scripts/jest/matchers/toWarnDev.js | 4 +- 25 files changed, 198 insertions(+), 413 deletions(-) delete mode 100644 packages/react-client/src/ReactServerContext.js delete mode 100644 packages/react-server/src/ReactServerContext.js delete mode 100644 packages/react/src/ReactServerContext.js create mode 100644 packages/shared/ReactServerContext.js create mode 100644 packages/shared/ReactServerContextRegistry.js diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 90a960e3a61..365565067f5 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -24,14 +24,14 @@ import { parseModel, } from './ReactFlightClientHostConfig'; -import {getOrCreateServerContext} from './ReactServerContext'; - import { REACT_LAZY_TYPE, REACT_ELEMENT_TYPE, REACT_PROVIDER_TYPE, } from 'shared/ReactSymbols'; +import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry'; + export type JSONValue = | number | null @@ -324,11 +324,6 @@ export function parseModelString( // When passed into React, we'll know how to suspend on this. return createLazyChunkWrapper(chunk); } - case '!': { - if (value === '!') { - return REACT_PROVIDER_TYPE; - } - } } return value; } diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js deleted file mode 100644 index 61af8e3ec31..00000000000 --- a/packages/react-client/src/ReactServerContext.js +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import { - REACT_PROVIDER_TYPE, - REACT_SERVER_CONTEXT_TYPE, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, -} from 'shared/ReactSymbols'; - -import type { - ReactServerContext, - ServerContextJSONValue, -} from 'shared/ReactTypes'; - -import ReactSharedInternals from 'shared/ReactSharedInternals'; - -import {enableServerContext} from 'shared/ReactFeatureFlags'; - -const ContextRegistry = ReactSharedInternals.ContextRegistry; - -export function createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = _createServerContext( - globalName, - defaultValue, - ); - } - const context = ContextRegistry[globalName]; - if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - context._defaultValue = defaultValue; - } else { - throw new Error(`ServerContext: ${globalName} already defined`); - } - return context; -} - -function _createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - const context: ReactServerContext = { - $$typeof: REACT_SERVER_CONTEXT_TYPE, - // As a workaround to support multiple concurrent renderers, we categorize - // some renderers as primary and others as secondary. We only expect - // there to be two concurrent renderers at most: React Native (primary) and - // Fabric (secondary); React DOM (primary) and React ART (secondary). - // Secondary renderers store their context values on separate fields. - _currentValue: defaultValue, - _currentValue2: defaultValue, - - _defaultValue: defaultValue, - - // Used to track how many concurrent renderers this context currently - // supports within in a single renderer. Such as parallel server rendering. - _threadCount: 0, - _definitionLoaded: false, - // These are circular - Provider: (null: any), - displayName: globalName, - }; - - context.Provider = { - $$typeof: REACT_PROVIDER_TYPE, - _context: context, - }; - - if (__DEV__) { - context._currentRenderer = null; - context._currentRenderer2 = null; - } - ContextRegistry[globalName] = context; - return context; -} - -// This function is called by FlightClient to create a server context sent from -// the server. Its possible that FlightClient is creating it before the -// definition is loaded on the server. We'll create it with a null default value -// if thats the case and when the definition loads it will set the correct -// default value. -export function getOrCreateServerContext(globalName: string) { - if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = _createServerContext( - globalName, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, - ); - } - return ContextRegistry[globalName]; -} diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index b4d1eaa1621..9197a4c3bfe 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -391,7 +391,9 @@ describe('ReactFlight', () => { expect(() => { ReactNoopFlightServer.render(); - }).toErrorDev('React elements are not allowed in ServerContext'); + }).toErrorDev('React elements are not allowed in ServerContext', { + withoutStack: true, + }); }); // @gate enableServerContext @@ -556,9 +558,9 @@ describe('ReactFlight', () => { function Bar() { return {React.useContext(ServerContext)}; } - const transport = ReactNoopFlightServer.render(, { - context: [['ServerContext', 'Override']], - }); + const transport = ReactNoopFlightServer.render(, {}, [ + ['ServerContext', 'Override'], + ]); act(() => { const flightModel = ReactNoopFlightClient.read(transport); diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 466d852fb69..30b3c863c79 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -104,9 +104,7 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext( - context: ReactContext | ReactServerContext, -): T { +function readContext(context: ReactContext): T { // For now we don't expose readContext usage in the hooks debugging info. return context._currentValue; } @@ -676,16 +674,12 @@ export function inspectHooks( return buildTree(rootStack, readHookLog, includeHooksSource); } -function setupContexts( - contextMap: Map | ReactServerContext, any>, - fiber: Fiber, -) { +function setupContexts(contextMap: Map, any>, fiber: Fiber) { let current = fiber; while (current) { if (current.tag === ContextProvider) { const providerType: ReactProviderType = current.type; - const context: ReactContext | ReactServerContext = - providerType._context; + const context: ReactContext = providerType._context; if (!contextMap.has(context)) { // Store the current value that we're going to restore later. contextMap.set(context, context._currentValue); @@ -697,9 +691,7 @@ function setupContexts( } } -function restoreContexts( - contextMap: Map | ReactServerContext, any>, -) { +function restoreContexts(contextMap: Map, any>) { contextMap.forEach((value, context) => (context._currentValue = value)); } diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index ddcf5759be4..41baa807955 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -2389,18 +2389,17 @@ describe('ReactDOMFizzServer', () => { // @gate enableServerContext && experimental it('supports ServerContext', async () => { - const {getOrCreateServerContext} = require('react/src/ReactServerContext'); - const ServerContext = getOrCreateServerContext('ServerContext'); - - let initialized = false; + let ServerContext; function inlineLazyServerContextInitialization() { - if (!initialized) { - initialized = true; - React.createServerContext('ServerContext', 'default'); + if (!ServerContext) { + console.log({ServerContext}); + ServerContext = React.createServerContext('ServerContext', 'default'); } + return ServerContext; } function Foo() { + inlineLazyServerContextInitialization(); return ( <> @@ -2420,14 +2419,11 @@ describe('ReactDOMFizzServer', () => { ); } function Bar() { - const context = React.useContext(ServerContext); - inlineLazyServerContextInitialization(); + const context = React.useContext(inlineLazyServerContextInitialization()); return {context}; } await act(async () => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; const {pipe} = ReactDOMFizzServer.renderToPipeableStream(); pipe(writable); }); diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 874c9642dc1..7e30b1ad1ee 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -780,7 +780,7 @@ class ReactDOMServerRenderer { suspenseDepth: number; contextIndex: number; - contextStack: Array | ReactServerContext>; + contextStack: Array>; contextValueStack: Array; contextProviderStack: ?Array>; // DEV-only @@ -838,8 +838,7 @@ class ReactDOMServerRenderer { pushProvider(provider: ReactProvider): void { const index = ++this.contextIndex; - const context: ReactContext | ReactServerContext = - provider.type._context; + const context: ReactContext = provider.type._context; const threadID = this.threadID; validateContextBounds(context, threadID); const previousValue = context[threadID]; @@ -864,8 +863,7 @@ class ReactDOMServerRenderer { } } - const context: ReactContext | ReactServerContext = this - .contextStack[index]; + const context: ReactContext = this.contextStack[index]; const previousValue = this.contextValueStack[index]; // "Hide" these null assignments from Flow by using `any` @@ -887,8 +885,7 @@ class ReactDOMServerRenderer { clearProviders(): void { // Restore any remaining providers on the stack to previous values for (let index = this.contextIndex; index >= 0; index--) { - const context: ReactContext | ReactServerContext = this - .contextStack[index]; + const context: ReactContext = this.contextStack[index]; const previousValue = this.contextValueStack[index]; context[this.threadID] = previousValue; } diff --git a/packages/react-dom/src/server/ReactPartialRendererContext.js b/packages/react-dom/src/server/ReactPartialRendererContext.js index bdcd9f4ba65..f51dcb27b14 100644 --- a/packages/react-dom/src/server/ReactPartialRendererContext.js +++ b/packages/react-dom/src/server/ReactPartialRendererContext.js @@ -44,7 +44,7 @@ function checkContextTypes(typeSpecs, values, location: string) { } export function validateContextBounds( - context: ReactContext | ReactServerContext, + context: ReactContext, threadID: ThreadID, ) { // If we don't have enough slots in this context to store this threadID, diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index cb1f362f79b..987c7ee27b7 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -64,16 +64,20 @@ type ServerContextJSONValue = type Options = { onError?: (error: mixed) => void, - context?: Array<[string, ServerContextJSONValue]>, }; -function render(model: ReactModel, options?: Options): Destination { +function render( + model: ReactModel, + options?: Options, + context?: Array<[string, ServerContextJSONValue]>, +): Destination { const destination: Destination = []; const bundlerConfig = undefined; const request = ReactNoopFlightServer.createRequest( model, bundlerConfig, options, + context, ); ReactNoopFlightServer.startWork(request); ReactNoopFlightServer.startFlowing(request, destination); diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index b8faaf44221..78accd1daad 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -3217,8 +3217,7 @@ function updateContextProvider( renderLanes: Lanes, ) { const providerType: ReactProviderType = workInProgress.type; - const context: ReactContext | ReactServerContext = - providerType._context; + const context: ReactContext = providerType._context; const newProps = workInProgress.pendingProps; const oldProps = workInProgress.memoizedProps; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 9a1d6945d4f..b35809a96cb 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -3217,8 +3217,7 @@ function updateContextProvider( renderLanes: Lanes, ) { const providerType: ReactProviderType = workInProgress.type; - const context: ReactContext | ReactServerContext = - providerType._context; + const context: ReactContext = providerType._context; const newProps = workInProgress.pendingProps; const oldProps = workInProgress.memoizedProps; diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 9a516a3fec2..13b75a56391 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -131,7 +131,7 @@ export function pushProvider( } export function popProvider( - context: ReactContext | ReactServerContext, + context: ReactContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; @@ -550,8 +550,7 @@ function propagateParentContextChanges( const oldProps = currentParent.memoizedProps; if (oldProps !== null) { const providerType: ReactProviderType = parent.type; - const context: ReactContext | ReactServerContext = - providerType._context; + const context: ReactContext = providerType._context; const newProps = parent.pendingProps; const newValue = newProps.value; diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 2a990c9b040..0909254b6f5 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -131,7 +131,7 @@ export function pushProvider( } export function popProvider( - context: ReactContext | ReactServerContext, + context: ReactContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; @@ -550,8 +550,7 @@ function propagateParentContextChanges( const oldProps = currentParent.memoizedProps; if (oldProps !== null) { const providerType: ReactProviderType = parent.type; - const context: ReactContext | ReactServerContext = - providerType._context; + const context: ReactContext = providerType._context; const newProps = parent.pendingProps; const newValue = newProps.value; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 686b2ae334a..cf0fb63f193 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -1491,6 +1491,7 @@ function handleError(root, thrownValue): void { ); } } + throwException( root, erroredWork.return, diff --git a/packages/react-reconciler/src/getComponentNameFromFiber.js b/packages/react-reconciler/src/getComponentNameFromFiber.js index a05595624b4..dec6c3d5caf 100644 --- a/packages/react-reconciler/src/getComponentNameFromFiber.js +++ b/packages/react-reconciler/src/getComponentNameFromFiber.js @@ -57,7 +57,7 @@ function getWrappedName( } // Keep in sync with shared/getComponentNameFromType -function getContextName(type: ReactContext | ReactServerContext) { +function getContextName(type: ReactContext) { return type.displayName || 'Context'; } diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js index c7286509b51..122a56c2a69 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js @@ -7,10 +7,8 @@ * @flow */ -import type { - ReactModel, - RequestOptions, -} from 'react-server/src/ReactFlightServer'; +import type {ReactModel} from 'react-server/src/ReactFlightServer'; +import type {ServerContextJSONValue} from 'shared/ReactTypes'; import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig'; import { @@ -19,12 +17,17 @@ import { startFlowing, } from 'react-server/src/ReactFlightServer'; +type Options = { + onError?: (error: mixed) => void, +}; + function renderToReadableStream( model: ReactModel, webpackMap: BundlerConfig, - options?: RequestOptions, + options?: Options, + context?: Array<[string, ServerContextJSONValue]>, ): ReadableStream { - const request = createRequest(model, webpackMap, options); + const request = createRequest(model, webpackMap, options, context); const stream = new ReadableStream({ start(controller) { startWork(request); diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js index 92abb8afae1..bb990c9e179 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js @@ -7,12 +7,10 @@ * @flow */ -import type { - ReactModel, - RequestOptions, -} from 'react-server/src/ReactFlightServer'; +import type {ReactModel} from 'react-server/src/ReactFlightServer'; import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig'; import type {Writable} from 'stream'; +import type {ServerContextJSONValue} from 'shared/ReactTypes'; import { createRequest, @@ -24,6 +22,10 @@ function createDrainHandler(destination, request) { return () => startFlowing(request, destination); } +type Options = { + onError?: (error: mixed) => void, +}; + type Controls = {| pipe(destination: T): T, |}; @@ -31,9 +33,10 @@ type Controls = {| function renderToPipeableStream( model: ReactModel, webpackMap: BundlerConfig, - options?: RequestOptions, + options?: Options, + context?: Array<[string, ServerContextJSONValue]>, ): Controls { - const request = createRequest(model, webpackMap, options); + const request = createRequest(model, webpackMap, options, context); let hasStartedFlowing = false; startWork(request); return { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index b1de0395784..b7448692185 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -38,7 +38,6 @@ import { isModuleReference, } from './ReactFlightServerConfig'; -import {getOrCreateServerContext} from './ReactServerContext'; import {Dispatcher, getCurrentCache, setCurrentCache} from './ReactFlightHooks'; import { pushProvider, @@ -56,11 +55,15 @@ import { REACT_MEMO_TYPE, REACT_PROVIDER_TYPE, REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, } from 'shared/ReactSymbols'; +import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import isArray from 'shared/isArray'; +import {createServerContext} from 'react'; + type ReactJSONValue = | string | boolean @@ -105,12 +108,12 @@ export type Request = { toJSON: (key: string, value: ReactModel) => ReactJSONValue, }; -export type RequestOptions = { +export type Options = { onError?: (error: mixed) => void, - context?: Array<[string, ServerContextJSONValue]>, }; const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; +const ContextRegistry = ReactSharedInternals.ContextRegistry; function defaultErrorHandler(error: mixed) { console['error'](error); @@ -124,7 +127,8 @@ const CLOSED = 2; export function createRequest( model: ReactModel, bundlerConfig: BundlerConfig, - options?: RequestOptions, + options?: Options, + context?: Array<[string, ServerContextJSONValue]>, ): Request { const pingedSegments = []; const onError = options ? options.onError : undefined; @@ -148,8 +152,8 @@ export function createRequest( }, }; request.pendingChunks++; - const context = createRootContext(options ? options.context : undefined); - const rootSegment = createSegment(request, model, context); + const rootContext = createRootContext(context); + const rootSegment = createSegment(request, model, rootContext); pingedSegments.push(rootSegment); return request; } @@ -224,8 +228,8 @@ function attemptResolveElement( REACT_PROVIDER_TYPE, type._context.displayName, key, - props, - type._context, + // Rely on __popProvider being serialized last to pop the provider. + {...props, __popProvider$$: type._context}, ]; } } @@ -457,8 +461,6 @@ export function resolveModelToJSON( switch (value) { case REACT_ELEMENT_TYPE: return '$'; - case REACT_PROVIDER_TYPE: - return '!'; case REACT_LAZY_TYPE: throw new Error( 'React Lazy Components are not yet supported on the server.', @@ -473,13 +475,15 @@ export function resolveModelToJSON( } else if (insideContextProps === parent && key === 'children') { isInsideContextValue = false; } - if (isReactElement(value) && isInsideContextValue) { - throw new Error('React elements are not allowed in ServerContext'); - } } // Resolve server components. while (isReactElement(value)) { + if (__DEV__) { + if (isInsideContextValue) { + console.error('React elements are not allowed in ServerContext'); + } + } // TODO: Concatenate keys of parents onto children. const element: React$Element = (value: any); try { @@ -517,8 +521,7 @@ export function resolveModelToJSON( if ( value.$$typeof === REACT_SERVER_CONTEXT_TYPE && - key === '4' && - parent[0] === REACT_PROVIDER_TYPE + key === '__popProvider$$' ) { popProvider((value: any)); if (__DEV__) { diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js deleted file mode 100644 index eacfbdf0640..00000000000 --- a/packages/react-server/src/ReactServerContext.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import { - REACT_PROVIDER_TYPE, - REACT_SERVER_CONTEXT_TYPE, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, -} from 'shared/ReactSymbols'; - -import type { - ReactServerContext, - ServerContextJSONValue, -} from 'shared/ReactTypes'; - -import ReactSharedInternals from 'shared/ReactSharedInternals'; - -import {enableServerContext} from 'shared/ReactFeatureFlags'; - -const ContextRegistry = ReactSharedInternals.ContextRegistry; - -export function createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = _createServerContext( - globalName, - defaultValue, - ); - } - const context = ContextRegistry[globalName]; - if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - context._defaultValue = defaultValue; - if ( - context._currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED - ) { - context._currentValue = defaultValue; - } - if ( - context._currentValue2 === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED - ) { - context._currentValue2 = defaultValue; - } - } else { - throw new Error(`ServerContext: ${globalName} already defined`); - } - return context; -} - -function _createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - const context: ReactServerContext = { - $$typeof: REACT_SERVER_CONTEXT_TYPE, - // As a workaround to support multiple concurrent renderers, we categorize - // some renderers as primary and others as secondary. We only expect - // there to be two concurrent renderers at most: React Native (primary) and - // Fabric (secondary); React DOM (primary) and React ART (secondary). - // Secondary renderers store their context values on separate fields. - _currentValue: defaultValue, - _currentValue2: defaultValue, - - _defaultValue: defaultValue, - - // Used to track how many concurrent renderers this context currently - // supports within in a single renderer. Such as parallel server rendering. - _threadCount: 0, - _definitionLoaded: false, - // These are circular - Provider: (null: any), - displayName: globalName, - }; - - context.Provider = { - $$typeof: REACT_PROVIDER_TYPE, - _context: context, - }; - - if (__DEV__) { - context._currentRenderer = null; - context._currentRenderer2 = null; - } - ContextRegistry[globalName] = context; - return context; -} - -// This function is called by FlightClient to create a server context sent from -// the server. Its possible that FlightClient is creating it before the -// definition is loaded on the server. We'll create it with a null default value -// if thats the case and when the definition loads it will set the correct -// default value. -export function getOrCreateServerContext(globalName: string) { - if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = _createServerContext( - globalName, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, - ); - } - return ContextRegistry[globalName]; -} diff --git a/packages/react/src/React.js b/packages/react/src/React.js index d13bf62941b..a762d7d83f1 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -32,7 +32,6 @@ import { isValidElement, } from './ReactElement'; import {createContext} from './ReactContext'; -import {createServerContext} from './ReactServerContext'; import {lazy} from './ReactLazy'; import {forwardRef} from './ReactForwardRef'; import {memo} from './ReactMemo'; @@ -62,6 +61,7 @@ import { createFactoryWithValidation, cloneElementWithValidation, } from './ReactElementValidator'; +import {createServerContext} from 'shared/ReactServerContext'; import {createMutableSource} from './ReactMutableSource'; import ReactSharedInternals from './ReactSharedInternals'; import {startTransition} from './ReactStartTransition'; diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js deleted file mode 100644 index a236de4b2a4..00000000000 --- a/packages/react/src/ReactServerContext.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import { - REACT_PROVIDER_TYPE, - REACT_SERVER_CONTEXT_TYPE, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, -} from 'shared/ReactSymbols'; - -import type { - ReactServerContext, - ServerContextJSONValue, -} from 'shared/ReactTypes'; - -import ReactSharedInternals from 'shared/ReactSharedInternals'; - -import {enableServerContext} from 'shared/ReactFeatureFlags'; - -const ContextRegistry = ReactSharedInternals.ContextRegistry; - -export function createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - let context; - if (ContextRegistry[globalName]) { - context = ContextRegistry[globalName]; - if ( - context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED - ) { - context._defaultValue = defaultValue; - } else { - throw new Error(`ServerContext: ${globalName} already defined`); - } - } else { - context = ContextRegistry[globalName] = _createServerContext( - globalName, - defaultValue, - ); - } - return context; -} - -function _createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - const context: ReactServerContext = { - $$typeof: REACT_SERVER_CONTEXT_TYPE, - // As a workaround to support multiple concurrent renderers, we categorize - // some renderers as primary and others as secondary. We only expect - // there to be two concurrent renderers at most: React Native (primary) and - // Fabric (secondary); React DOM (primary) and React ART (secondary). - // Secondary renderers store their context values on separate fields. - _currentValue: defaultValue, - _currentValue2: defaultValue, - - _defaultValue: defaultValue, - - // Used to track how many concurrent renderers this context currently - // supports within in a single renderer. Such as parallel server rendering. - _threadCount: 0, - _definitionLoaded: false, - // These are circular - Provider: (null: any), - displayName: globalName, - }; - - context.Provider = { - $$typeof: REACT_PROVIDER_TYPE, - _context: context, - }; - - if (__DEV__) { - context._currentRenderer = null; - context._currentRenderer2 = null; - } - ContextRegistry[globalName] = context; - return context; -} - -// This function is called by FlightClient to create a server context sent from -// the server. Its possible that FlightClient is creating it before the -// definition is loaded on the server. We'll create it with a null default value -// if thats the case and when the definition loads it will set the correct -// default value. -export function getOrCreateServerContext(globalName: string) { - if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = _createServerContext( - globalName, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, - ); - } - return ContextRegistry[globalName]; -} diff --git a/packages/shared/ReactServerContext.js b/packages/shared/ReactServerContext.js new file mode 100644 index 00000000000..b0a5e23b230 --- /dev/null +++ b/packages/shared/ReactServerContext.js @@ -0,0 +1,92 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import { + REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, +} from 'shared/ReactSymbols'; + +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; + +import {enableServerContext} from 'shared/ReactFeatureFlags'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; + +const ContextRegistry = ReactSharedInternals.ContextRegistry; + +export function createServerContext( + globalName: string, + defaultValue: T, +): ReactServerContext { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + let wasDefined = true; + if (!ContextRegistry[globalName]) { + wasDefined = false; + const context: ReactServerContext = { + $$typeof: REACT_SERVER_CONTEXT_TYPE, + + // As a workaround to support multiple concurrent renderers, we categorize + // some renderers as primary and others as secondary. We only expect + // there to be two concurrent renderers at most: React Native (primary) and + // Fabric (secondary); React DOM (primary) and React ART (secondary). + // Secondary renderers store their context values on separate fields. + _currentValue: defaultValue, + _currentValue2: defaultValue, + + _defaultValue: defaultValue, + + // Used to track how many concurrent renderers this context currently + // supports within in a single renderer. Such as parallel server rendering. + _threadCount: 0, + _definitionLoaded: false, + // These are circular + Provider: (null: any), + Consumer: (null: any), + _globalName: globalName, + }; + + context.Provider = { + $$typeof: REACT_PROVIDER_TYPE, + _context: context, + }; + + if (__DEV__) { + let hasWarnedAboutUsingConsumer; + context._currentRenderer = null; + context._currentRenderer2 = null; + Object.defineProperties( + context, + ({ + Consumer: { + get() { + console.error( + 'Consumer pattern is not supported by ReactServerContext', + ); + return null; + }, + }, + }: any), + ); + } + ContextRegistry[globalName] = context; + } + + const context = ContextRegistry[globalName]; + if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + context._defaultValue = defaultValue; + } else if (wasDefined) { + throw new Error(`ServerContext: ${globalName} already defined`); + } + return context; +} diff --git a/packages/shared/ReactServerContextRegistry.js b/packages/shared/ReactServerContextRegistry.js new file mode 100644 index 00000000000..d721a3750ad --- /dev/null +++ b/packages/shared/ReactServerContextRegistry.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; +import {createServerContext} from './ReactServerContext'; + +const ContextRegistry = ReactSharedInternals.ContextRegistry; + +export function getOrCreateServerContext(globalName: string) { + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = createServerContext( + globalName, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, + ); + } + return ContextRegistry[globalName]; +} diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index d939b7e05fb..44305beac1c 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -12,8 +12,8 @@ export type ReactNode = | ReactPortal | ReactText | ReactFragment - | ReactProvider - | ReactConsumer; + | ReactProvider> + | ReactConsumer>; export type ReactEmpty = null | void | boolean; @@ -25,7 +25,7 @@ export type ReactText = string | number; export type ReactProvider = { $$typeof: Symbol | number, - type: ReactProviderType | ReactServerProviderType, + type: ReactProviderType, key: null | string, ref: null, props: { @@ -84,17 +84,10 @@ export type ServerContextJSONValue = | $ReadOnlyArray | {+[key: string]: ServerContextJSONValue}; -export type ReactServerContext = { - $$typeof: Symbol | number, - Provider: ReactServerProviderType, +export type ReactServerContext = ReactContext & { _defaultValue: T, - _currentValue: T, - _currentValue2: T, - _currentRenderer?: Object | null, - _currentRenderer2?: Object | null, - _threadCount: number, _definitionLoaded: boolean, - +displayName: string, + _globalName: string, ... }; diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 9a1cee36a13..d07a1b35266 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -45,7 +45,7 @@ function getWrappedName( } // Keep in sync with react-reconciler/getComponentNameFromFiber -function getContextName(type: ReactContext | ReactServerContext) { +function getContextName(type: ReactContext) { return type.displayName || 'Context'; } diff --git a/scripts/jest/matchers/toWarnDev.js b/scripts/jest/matchers/toWarnDev.js index dd395d30d11..5e2a144b4e2 100644 --- a/scripts/jest/matchers/toWarnDev.js +++ b/scripts/jest/matchers/toWarnDev.js @@ -86,9 +86,7 @@ const createMatcherFor = (consoleMethod, matcherName) => // doesn't match the number of arguments. // We'll fail the test if it happens. let argIndex = 0; - if (format.replace) { - format.replace(/%s/g, () => argIndex++); - } + format.replace(/%s/g, () => argIndex++); if (argIndex !== args.length) { lastWarningWithMismatchingFormat = { format, From de7f685e3a2fd1930c6d90ad174a3aab7efc3ddf Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 13:36:19 -0500 Subject: [PATCH 23/83] remove unions --- .../react-devtools-shared/src/backend/types.js | 2 +- .../src/server/ReactPartialRendererHooks.js | 4 +--- .../react-reconciler/src/ReactFiberHooks.new.js | 14 +++++++------- .../react-reconciler/src/ReactFiberHooks.old.js | 14 +++++++------- .../src/ReactFiberNewContext.new.js | 12 +++++------- .../src/ReactFiberNewContext.old.js | 12 +++++------- .../react-reconciler/src/ReactFiberScope.new.js | 8 +++----- .../react-reconciler/src/ReactFiberScope.old.js | 8 +++----- .../react-reconciler/src/ReactInternalTypes.js | 4 ++-- packages/react-server/src/ReactFizzHooks.js | 4 +--- packages/react-server/src/ReactFizzNewContext.js | 12 ++++-------- packages/react-server/src/ReactFlightHooks.js | 4 +--- packages/shared/ReactTypes.js | 4 ++-- 13 files changed, 42 insertions(+), 60 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index 19327019e59..ec6b09d255e 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -89,7 +89,7 @@ export type FindNativeNodesForFiberID = (id: number) => ?Array; export type ReactProviderType = { $$typeof: Symbol | number, - _context: ReactContext | ReactServerContext, + _context: ReactContext, ... }; diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index e2280fbd48b..57c63f402e0 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -223,9 +223,7 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext( - context: ReactContext | ReactServerContext, -): T { +function readContext(context: ReactContext): T { const threadID = currentPartialRenderer.threadID; validateContextBounds(context, threadID); if (__DEV__) { diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 745593243d9..4d45a2d2bca 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -2519,7 +2519,7 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2667,7 +2667,7 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2809,7 +2809,7 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2951,7 +2951,7 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, @@ -3094,7 +3094,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3253,7 +3253,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3412,7 +3412,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 48dceb85a1d..72d26adb5da 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -2519,7 +2519,7 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2667,7 +2667,7 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2809,7 +2809,7 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2951,7 +2951,7 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, @@ -3094,7 +3094,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3253,7 +3253,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3412,7 +3412,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 13b75a56391..350b13f86dd 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -90,7 +90,7 @@ export function exitDisallowedContextReadInDEV(): void { export function pushProvider( providerFiber: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, nextValue: T, ): void { if (isPrimaryRenderer) { @@ -195,7 +195,7 @@ export function scheduleContextWorkOnParentPath( export function propagateContextChange( workInProgress: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, renderLanes: Lanes, ): void { if (enableLazyContextPropagation) { @@ -216,7 +216,7 @@ export function propagateContextChange( function propagateContextChange_eager( workInProgress: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, renderLanes: Lanes, ): void { // Only used by eager implementation @@ -383,7 +383,7 @@ function propagateContextChanges( const dependency = dep; const consumer = fiber; findContext: for (let i = 0; i < contexts.length; i++) { - const context: ReactContext | ReactServerContext = contexts[i]; + const context: ReactContext = contexts[i]; // Check if the context matches. // TODO: Compare selected values to bail out early. if (dependency.context === context) { @@ -653,9 +653,7 @@ export function prepareToReadContext( } } -export function readContext( - context: ReactContext | ReactServerContext, -): T { +export function readContext(context: ReactContext): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 0909254b6f5..c61e516ac94 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -90,7 +90,7 @@ export function exitDisallowedContextReadInDEV(): void { export function pushProvider( providerFiber: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, nextValue: T, ): void { if (isPrimaryRenderer) { @@ -195,7 +195,7 @@ export function scheduleContextWorkOnParentPath( export function propagateContextChange( workInProgress: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, renderLanes: Lanes, ): void { if (enableLazyContextPropagation) { @@ -216,7 +216,7 @@ export function propagateContextChange( function propagateContextChange_eager( workInProgress: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, renderLanes: Lanes, ): void { // Only used by eager implementation @@ -383,7 +383,7 @@ function propagateContextChanges( const dependency = dep; const consumer = fiber; findContext: for (let i = 0; i < contexts.length; i++) { - const context: ReactContext | ReactServerContext = contexts[i]; + const context: ReactContext = contexts[i]; // Check if the context matches. // TODO: Compare selected values to bail out early. if (dependency.context === context) { @@ -653,9 +653,7 @@ export function prepareToReadContext( } } -export function readContext( - context: ReactContext | ReactServerContext, -): T { +export function readContext(context: ReactContext): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. diff --git a/packages/react-reconciler/src/ReactFiberScope.new.js b/packages/react-reconciler/src/ReactFiberScope.new.js index 491e82d89d3..9455af19c7f 100644 --- a/packages/react-reconciler/src/ReactFiberScope.new.js +++ b/packages/react-reconciler/src/ReactFiberScope.new.js @@ -111,7 +111,7 @@ function collectFirstScopedNodeFromChildren( function collectNearestContextValues( node: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, childContextValues: Array, ): void { if (node.tag === ContextProvider && node.type._context === context) { @@ -131,7 +131,7 @@ function collectNearestContextValues( function collectNearestChildContextValues( startingChild: Fiber | null, - context: ReactContext | ReactServerContext, + context: ReactContext, childContextValues: Array, ): void { let child = startingChild; @@ -177,9 +177,7 @@ function containsNode(node: Object): boolean { return false; } -function getChildContextValues( - context: ReactContext | ReactServerContext, -): Array { +function getChildContextValues(context: ReactContext): Array { const currentFiber = getInstanceFromScope(this); if (currentFiber === null) { return []; diff --git a/packages/react-reconciler/src/ReactFiberScope.old.js b/packages/react-reconciler/src/ReactFiberScope.old.js index 491e82d89d3..9455af19c7f 100644 --- a/packages/react-reconciler/src/ReactFiberScope.old.js +++ b/packages/react-reconciler/src/ReactFiberScope.old.js @@ -111,7 +111,7 @@ function collectFirstScopedNodeFromChildren( function collectNearestContextValues( node: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, childContextValues: Array, ): void { if (node.tag === ContextProvider && node.type._context === context) { @@ -131,7 +131,7 @@ function collectNearestContextValues( function collectNearestChildContextValues( startingChild: Fiber | null, - context: ReactContext | ReactServerContext, + context: ReactContext, childContextValues: Array, ): void { let child = startingChild; @@ -177,9 +177,7 @@ function containsNode(node: Object): boolean { return false; } -function getChildContextValues( - context: ReactContext | ReactServerContext, -): Array { +function getChildContextValues(context: ReactContext): Array { const currentFiber = getInstanceFromScope(this); if (currentFiber === null) { return []; diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 83608591f36..c1f2a4d6629 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -48,7 +48,7 @@ export type HookType = | 'useCacheRefresh'; export type ContextDependency = { - context: ReactContext | ReactServerContext, + context: ReactContext, next: ContextDependency | null, memoizedValue: T, ... @@ -340,7 +340,7 @@ type Dispatch
= A => void; export type Dispatcher = {| getCacheSignal?: () => AbortSignal, getCacheForType?: (resourceType: () => T) => T, - readContext(context: ReactContext | ReactServerContext): T, + readContext(context: ReactContext): T, useState(initialState: (() => S) | S): [S, Dispatch>], useReducer( reducer: (S, A) => S, diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index a3a79a93fbf..f2d0548c215 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -243,9 +243,7 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext( - context: ReactContext | ReactServerContext, -): T { +function readContext(context: ReactContext): T { if (__DEV__) { if (isInHookUserCodeInDev) { console.error( diff --git a/packages/react-server/src/ReactFizzNewContext.js b/packages/react-server/src/ReactFizzNewContext.js index c333b5681de..f65a065367f 100644 --- a/packages/react-server/src/ReactFizzNewContext.js +++ b/packages/react-server/src/ReactFizzNewContext.js @@ -23,7 +23,7 @@ if (__DEV__) { type ContextNode = { parent: null | ContextNode, depth: number, // Short hand to compute the depth of the tree at this node. - context: ReactContext | ReactServerContext, + context: ReactContext, parentValue: T, value: T, }; @@ -179,7 +179,7 @@ export function switchContext(newSnapshot: ContextSnapshot): void { } export function pushProvider( - context: ReactContext | ReactServerContext, + context: ReactContext, nextValue: T, ): ContextSnapshot { let prevValue; @@ -228,9 +228,7 @@ export function pushProvider( return newNode; } -export function popProvider( - context: ReactContext | ReactServerContext, -): ContextSnapshot { +export function popProvider(context: ReactContext): ContextSnapshot { const prevSnapshot = currentActiveSnapshot; if (prevSnapshot === null) { @@ -298,9 +296,7 @@ export function getActiveContext(): ContextSnapshot { return currentActiveSnapshot; } -export function readContext( - context: ReactContext | ReactServerContext, -): T { +export function readContext(context: ReactContext): T { const value = isPrimaryRenderer ? context._currentValue : context._currentValue2; diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index 0b12e00bfa3..f8c5c833cab 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -16,9 +16,7 @@ import type { import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; import {readContext as readContextImpl} from './ReactFlightNewContext'; -function readContext( - context: ReactContext | ReactServerContext, -): T { +function readContext(context: ReactContext): T { if (__DEV__) { if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { console.error('Only ServerContext is supported in Flight'); diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 44305beac1c..e3f37580d7d 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -38,7 +38,7 @@ export type ReactProvider = { export type ReactProviderType = { $$typeof: Symbol | number, - _context: ReactContext | ReactServerContext, + _context: ReactContext, ... }; @@ -50,7 +50,7 @@ export type ReactServerProviderType = { export type ReactConsumer = { $$typeof: Symbol | number, - type: ReactContext | ReactServerContext, + type: ReactContext, key: null | string, ref: null, props: { From 079691e97735ef524b97578f6d8e022794fdebdf Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 13:41:42 -0500 Subject: [PATCH 24/83] Lint --- packages/react-debug-tools/src/ReactDebugHooks.js | 1 - packages/react-devtools-shared/src/backend/types.js | 6 +----- packages/react-dom/src/server/ReactPartialRenderer.js | 6 +----- .../react-dom/src/server/ReactPartialRendererContext.js | 2 +- .../react-dom/src/server/ReactPartialRendererHooks.js | 1 - packages/react-reconciler/src/ReactFiberBeginWork.new.js | 6 +----- packages/react-reconciler/src/ReactFiberBeginWork.old.js | 6 +----- packages/react-reconciler/src/ReactFiberHooks.new.js | 1 - packages/react-reconciler/src/ReactFiberHooks.old.js | 1 - packages/react-reconciler/src/ReactFiberScope.new.js | 1 - packages/react-reconciler/src/ReactFiberScope.old.js | 1 - packages/react-reconciler/src/ReactInternalTypes.js | 1 - .../react-reconciler/src/getComponentNameFromFiber.js | 6 +----- packages/react-server/src/ReactFizzHooks.js | 1 - packages/react-server/src/ReactFlightServer.js | 9 +-------- packages/shared/ReactServerContext.js | 9 ++++++--- packages/shared/getComponentNameFromType.js | 6 +----- 17 files changed, 14 insertions(+), 50 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 30b3c863c79..f112f340e7a 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -12,7 +12,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, ReactProviderType, } from 'shared/ReactTypes'; import type { diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index ec6b09d255e..96261a03bfd 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactContext, - ReactServerContext, - Wakeable, -} from 'shared/ReactTypes'; +import type {ReactContext, Wakeable} from 'shared/ReactTypes'; import type {Source} from 'shared/ReactElementType'; import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; import type { diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 7e30b1ad1ee..5128ae187d9 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -10,11 +10,7 @@ import type {ThreadID} from './ReactThreadIDAllocator'; import type {ReactElement} from 'shared/ReactElementType'; import type {LazyComponent} from 'react/src/ReactLazy'; -import type { - ReactProvider, - ReactContext, - ReactServerContext, -} from 'shared/ReactTypes'; +import type {ReactProvider, ReactContext} from 'shared/ReactTypes'; import * as React from 'react'; import isArray from 'shared/isArray'; diff --git a/packages/react-dom/src/server/ReactPartialRendererContext.js b/packages/react-dom/src/server/ReactPartialRendererContext.js index f51dcb27b14..65af591e365 100644 --- a/packages/react-dom/src/server/ReactPartialRendererContext.js +++ b/packages/react-dom/src/server/ReactPartialRendererContext.js @@ -8,7 +8,7 @@ */ import type {ThreadID} from './ReactThreadIDAllocator'; -import type {ReactContext, ReactServerContext} from 'shared/ReactTypes'; +import type {ReactContext} from 'shared/ReactTypes'; import {disableLegacyContext} from 'shared/ReactFeatureFlags'; import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols'; diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 57c63f402e0..eb1eb5c3dca 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -14,7 +14,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, } from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 78accd1daad..9b8de1df801 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactProviderType, - ReactContext, - ReactServerContext, -} from 'shared/ReactTypes'; +import type {ReactProviderType, ReactContext} from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {TypeOfMode} from './ReactTypeOfMode'; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index b35809a96cb..457088598e5 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactProviderType, - ReactContext, - ReactServerContext, -} from 'shared/ReactTypes'; +import type {ReactProviderType, ReactContext} from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {TypeOfMode} from './ReactTypeOfMode'; diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 4d45a2d2bca..26ed72cf89b 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -12,7 +12,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.new'; diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 72d26adb5da..6f5bcc1fdeb 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -12,7 +12,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.old'; diff --git a/packages/react-reconciler/src/ReactFiberScope.new.js b/packages/react-reconciler/src/ReactFiberScope.new.js index 9455af19c7f..9fe9d09572a 100644 --- a/packages/react-reconciler/src/ReactFiberScope.new.js +++ b/packages/react-reconciler/src/ReactFiberScope.new.js @@ -12,7 +12,6 @@ import type { ReactScopeInstance, ReactContext, ReactScopeQuery, - ReactServerContext, } from 'shared/ReactTypes'; import { diff --git a/packages/react-reconciler/src/ReactFiberScope.old.js b/packages/react-reconciler/src/ReactFiberScope.old.js index 9455af19c7f..9fe9d09572a 100644 --- a/packages/react-reconciler/src/ReactFiberScope.old.js +++ b/packages/react-reconciler/src/ReactFiberScope.old.js @@ -12,7 +12,6 @@ import type { ReactScopeInstance, ReactContext, ReactScopeQuery, - ReactServerContext, } from 'shared/ReactTypes'; import { diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index c1f2a4d6629..4037f229e97 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -11,7 +11,6 @@ import type {Source} from 'shared/ReactElementType'; import type { RefObject, ReactContext, - ReactServerContext, MutableSourceSubscribeFn, MutableSourceGetSnapshotFn, MutableSourceVersion, diff --git a/packages/react-reconciler/src/getComponentNameFromFiber.js b/packages/react-reconciler/src/getComponentNameFromFiber.js index dec6c3d5caf..5cb87189c97 100644 --- a/packages/react-reconciler/src/getComponentNameFromFiber.js +++ b/packages/react-reconciler/src/getComponentNameFromFiber.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactContext, - ReactServerContext, - ReactProviderType, -} from 'shared/ReactTypes'; +import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; import { FunctionComponent, diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index f2d0548c215..ffaae314104 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -14,7 +14,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, } from 'shared/ReactTypes'; import type {ResponseState} from './ReactServerFormatConfig'; diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index b7448692185..92730f6764d 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -16,10 +16,7 @@ import type { ModuleKey, } from './ReactFlightServerConfig'; import type {ContextSnapshot} from './ReactFlightNewContext'; -import type { - ReactServerContext, - ServerContextJSONValue, -} from 'shared/ReactTypes'; +import type {ServerContextJSONValue} from 'shared/ReactTypes'; import { scheduleWork, @@ -55,15 +52,12 @@ import { REACT_MEMO_TYPE, REACT_PROVIDER_TYPE, REACT_SERVER_CONTEXT_TYPE, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, } from 'shared/ReactSymbols'; import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import isArray from 'shared/isArray'; -import {createServerContext} from 'react'; - type ReactJSONValue = | string | boolean @@ -113,7 +107,6 @@ export type Options = { }; const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; -const ContextRegistry = ReactSharedInternals.ContextRegistry; function defaultErrorHandler(error: mixed) { console['error'](error); diff --git a/packages/shared/ReactServerContext.js b/packages/shared/ReactServerContext.js index b0a5e23b230..9b1d9120e71 100644 --- a/packages/shared/ReactServerContext.js +++ b/packages/shared/ReactServerContext.js @@ -70,9 +70,12 @@ export function createServerContext( ({ Consumer: { get() { - console.error( - 'Consumer pattern is not supported by ReactServerContext', - ); + if (!hasWarnedAboutUsingConsumer) { + console.error( + 'Consumer pattern is not supported by ReactServerContext', + ); + hasWarnedAboutUsingConsumer = true; + } return null; }, }, diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index d07a1b35266..36432e56aca 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -8,11 +8,7 @@ */ import type {LazyComponent} from 'react/src/ReactLazy'; -import type { - ReactContext, - ReactServerContext, - ReactProviderType, -} from 'shared/ReactTypes'; +import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; import { REACT_CONTEXT_TYPE, From cb6668746c2672c5720909c5a9ff1b2283043f59 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 14:28:11 -0500 Subject: [PATCH 25/83] fix oopsies (tests/lint/mismatching arguments/signatures --- .../src/__tests__/ReactDOMFizzServer-test.js | 1 - .../src/ReactNoopFlightServer.js | 2 +- .../src/ReactFlightDOMServerBrowser.js | 7 ++++- .../src/ReactFlightDOMServerNode.js | 7 ++++- .../ReactFlightNativeRelayServerHostConfig.js | 1 + .../react-server/src/ReactFlightServer.js | 26 ++++++++++--------- packages/shared/ReactServerContext.js | 10 +++++++ packages/shared/ReactTypes.js | 4 +-- scripts/error-codes/codes.json | 4 +-- 9 files changed, 41 insertions(+), 21 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 41baa807955..9ba18b834f4 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -2392,7 +2392,6 @@ describe('ReactDOMFizzServer', () => { let ServerContext; function inlineLazyServerContextInitialization() { if (!ServerContext) { - console.log({ServerContext}); ServerContext = React.createServerContext('ServerContext', 'default'); } return ServerContext; diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index 987c7ee27b7..ee3de448217 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -76,7 +76,7 @@ function render( const request = ReactNoopFlightServer.createRequest( model, bundlerConfig, - options, + options ? options.onError : undefined, context, ); ReactNoopFlightServer.startWork(request); diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js index 122a56c2a69..7874782e912 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js @@ -27,7 +27,12 @@ function renderToReadableStream( options?: Options, context?: Array<[string, ServerContextJSONValue]>, ): ReadableStream { - const request = createRequest(model, webpackMap, options, context); + const request = createRequest( + model, + webpackMap, + options ? options.onError : undefined, + context, + ); const stream = new ReadableStream({ start(controller) { startWork(request); diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js index bb990c9e179..c088725f259 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js @@ -36,7 +36,12 @@ function renderToPipeableStream( options?: Options, context?: Array<[string, ServerContextJSONValue]>, ): Controls { - const request = createRequest(model, webpackMap, options, context); + const request = createRequest( + model, + webpackMap, + options ? options.onError : undefined, + context, + ); let hasStartedFlowing = false; startWork(request); return { diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js index 45f82f3be64..0624e693cfe 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js @@ -103,6 +103,7 @@ function convertModelToJSON( } return json; } + export function processModelChunk( request: Request, id: number, diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 92730f6764d..34a1015e346 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -16,7 +16,10 @@ import type { ModuleKey, } from './ReactFlightServerConfig'; import type {ContextSnapshot} from './ReactFlightNewContext'; -import type {ServerContextJSONValue} from 'shared/ReactTypes'; +import type { + ServerContextJSONValue, + ReactServerContext, +} from 'shared/ReactTypes'; import { scheduleWork, @@ -120,11 +123,10 @@ const CLOSED = 2; export function createRequest( model: ReactModel, bundlerConfig: BundlerConfig, - options?: Options, + onError: void | ((error: mixed) => void), context?: Array<[string, ServerContextJSONValue]>, ): Request { const pingedSegments = []; - const onError = options ? options.onError : undefined; const request = { status: OPEN, fatalError: null, @@ -211,7 +213,7 @@ function attemptResolveElement( return true; }); if (extraKeys.length !== 0) { - throw new Error( + console.error( 'ServerContext can only have a value prop and children. Found: ' + JSON.stringify(extraKeys), ); @@ -219,7 +221,7 @@ function attemptResolveElement( } return [ REACT_PROVIDER_TYPE, - type._context.displayName, + type._context._globalName, key, // Rely on __popProvider being serialized last to pop the provider. {...props, __popProvider$$: type._context}, @@ -513,6 +515,7 @@ export function resolveModelToJSON( } if ( + value && value.$$typeof === REACT_SERVER_CONTEXT_TYPE && key === '__popProvider$$' ) { @@ -887,18 +890,17 @@ export function startFlowing(request: Request, destination: Destination): void { function importServerContexts( contexts?: Array<[string, ServerContextJSONValue]>, ) { - const prevContext = getActiveContext(); - switchContext(rootContextSnapshot); - const registry: {[name: string]: ReactServerContext} = {}; if (contexts) { + const prevContext = getActiveContext(); + switchContext(rootContextSnapshot); for (let i = 0; i < contexts.length; i++) { const [name, value] = contexts[i]; const context = getOrCreateServerContext(name); pushProvider(context, value); - registry[name] = context; } + const importedContext = getActiveContext(); + switchContext(prevContext); + return importedContext; } - const importedContext = getActiveContext(); - switchContext(prevContext); - return importedContext; + return rootContextSnapshot; } diff --git a/packages/shared/ReactServerContext.js b/packages/shared/ReactServerContext.js index 9b1d9120e71..a45c56afe7d 100644 --- a/packages/shared/ReactServerContext.js +++ b/packages/shared/ReactServerContext.js @@ -88,6 +88,16 @@ export function createServerContext( const context = ContextRegistry[globalName]; if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { context._defaultValue = defaultValue; + if ( + context._currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { + context._currentValue = defaultValue; + } + if ( + context._currentValue2 === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { + context._currentValue2 = defaultValue; + } } else if (wasDefined) { throw new Error(`ServerContext: ${globalName} already defined`); } diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index e3f37580d7d..789a0ee3689 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -12,8 +12,8 @@ export type ReactNode = | ReactPortal | ReactText | ReactFragment - | ReactProvider> - | ReactConsumer>; + | ReactProvider + | ReactConsumer; export type ReactEmpty = null | void | boolean; diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index aef64d5e58b..af8d9ad78ca 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -410,7 +410,5 @@ "422": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", "423": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", "424": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", - "425": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", - "426": "ServerContext can only have a value prop and children. Found: %s", - "427": "React elements are not allowed in ServerContext" + "425": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering." } From 0946c5d0261a4d100112a77721d9190b8a901f33 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 14:30:28 -0500 Subject: [PATCH 26/83] lint again --- packages/react-server/src/ReactFlightServer.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 34a1015e346..5b966bf7609 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -16,10 +16,7 @@ import type { ModuleKey, } from './ReactFlightServerConfig'; import type {ContextSnapshot} from './ReactFlightNewContext'; -import type { - ServerContextJSONValue, - ReactServerContext, -} from 'shared/ReactTypes'; +import type {ServerContextJSONValue} from 'shared/ReactTypes'; import { scheduleWork, @@ -214,8 +211,8 @@ function attemptResolveElement( }); if (extraKeys.length !== 0) { console.error( - 'ServerContext can only have a value prop and children. Found: ' + - JSON.stringify(extraKeys), + 'ServerContext can only have a value prop and children. Found: %s', + JSON.stringify(extraKeys), ); } } From 64c6477bf2589434b38fb10db29fb7ed8abcd9ec Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 14:41:50 -0500 Subject: [PATCH 27/83] replace-fork --- packages/react-reconciler/src/ReactFiberWorkLoop.old.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 7e56fa734b9..b823ed91331 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -1491,6 +1491,7 @@ function handleError(root, thrownValue): void { ); } } + throwException( root, erroredWork.return, From 1f5e888c363dc5c0dbf9a11be64db9825670a004 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 14:45:46 -0500 Subject: [PATCH 28/83] remove extraneous change --- packages/react-noop-renderer/src/ReactNoopFlightClient.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-noop-renderer/src/ReactNoopFlightClient.js b/packages/react-noop-renderer/src/ReactNoopFlightClient.js index b4e2e830fc8..df586c6efb2 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightClient.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightClient.js @@ -40,8 +40,7 @@ function read(source: Source): T { processStringChunk(response, source[i], 0); } close(response); - const root = response.readRoot(); - return root; + return response.readRoot(); } export {read}; From 8323eb8885e71534835d23b370a7204775b6f02b Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Feb 2022 16:06:48 -0500 Subject: [PATCH 29/83] rebase --- .../react-client/src/ReactFlightClient.js | 29 ++- .../src/__tests__/ReactFlight-test.js | 198 ++++++++++++++ .../react-debug-tools/src/ReactDebugHooks.js | 18 +- .../src/backend/types.js | 4 +- .../src/server/ReactPartialRendererContext.js | 4 +- .../src/server/ReactPartialRendererHooks.js | 19 +- .../src/ReactNoopFlightClient.js | 3 +- .../src/ReactNoopFlightServer.js | 13 +- .../src/ReactFiberBeginWork.new.js | 9 +- .../src/ReactFiberBeginWork.old.js | 9 +- .../src/ReactFiberHooks.new.js | 70 ++++- .../src/ReactFiberHooks.old.js | 70 ++++- .../src/ReactFiberNewContext.new.js | 38 +-- .../src/ReactFiberNewContext.old.js | 37 +-- .../src/ReactFiberScope.new.js | 13 +- .../src/ReactFiberScope.old.js | 13 +- .../src/ReactFiberWorkLoop.new.js | 1 - .../src/ReactInternalTypes.js | 16 +- .../ReactFlightDOMRelayServerHostConfig.js | 37 +-- .../src/ReactFlightDOMServerBrowser.js | 17 +- .../src/ReactFlightDOMServerNode.js | 17 +- .../ReactFlightNativeRelayServerHostConfig.js | 5 +- packages/react-server/src/ReactFizzHooks.js | 17 +- .../react-server/src/ReactFizzNewContext.js | 18 +- packages/react-server/src/ReactFlightHooks.js | 113 ++++++++ .../react-server/src/ReactFlightNewContext.js | 246 ++++++++++++++++++ .../react-server/src/ReactFlightServer.js | 190 +++++++++----- .../src/ReactFlightServerConfigStream.js | 5 +- packages/react/index.classic.fb.js | 4 +- packages/react/index.experimental.js | 4 +- packages/react/index.js | 2 + packages/react/index.modern.fb.js | 4 +- packages/react/index.stable.js | 4 +- packages/react/src/React.js | 4 + packages/react/src/ReactHooks.js | 10 + packages/react/src/ReactServerContext.js | 63 +++++ packages/shared/ReactTypes.js | 30 ++- packages/shared/getComponentNameFromType.js | 2 +- scripts/error-codes/codes.json | 4 +- 39 files changed, 1146 insertions(+), 214 deletions(-) create mode 100644 packages/react-server/src/ReactFlightHooks.js create mode 100644 packages/react-server/src/ReactFlightNewContext.js create mode 100644 packages/react/src/ReactServerContext.js diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 9d5ac3680a8..acc3436f7c4 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -24,7 +24,13 @@ import { parseModel, } from './ReactFlightClientHostConfig'; -import {REACT_LAZY_TYPE, REACT_ELEMENT_TYPE} from 'shared/ReactSymbols'; +import {getOrCreateContextByName} from 'react/src/ReactServerContext'; + +import { + REACT_LAZY_TYPE, + REACT_ELEMENT_TYPE, + REACT_PROVIDER_TYPE, +} from 'shared/ReactSymbols'; export type JSONValue = | number @@ -318,6 +324,11 @@ export function parseModelString( // When passed into React, we'll know how to suspend on this. return createLazyChunkWrapper(chunk); } + case '!': { + if (value === '!') { + return REACT_PROVIDER_TYPE; + } + } } return value; } @@ -327,10 +338,18 @@ export function parseModelTuple( value: {+[key: string]: JSONValue} | $ReadOnlyArray, ): any { const tuple: [mixed, mixed, mixed, mixed] = (value: any); - if (tuple[0] === REACT_ELEMENT_TYPE) { - // TODO: Consider having React just directly accept these arrays as elements. - // Or even change the ReactElement type to be an array. - return createElement(tuple[1], tuple[2], tuple[3]); + + switch (tuple[0]) { + case REACT_ELEMENT_TYPE: + // TODO: Consider having React just directly accept these arrays as elements. + // Or even change the ReactElement type to be an array. + return createElement(tuple[1], tuple[2], tuple[3]); + case REACT_PROVIDER_TYPE: + return createElement( + getOrCreateContextByName((tuple[1]: any)).Provider, + tuple[2], + tuple[3], + ); } return value; } diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 8d0fbe1609d..2db829fd7cd 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -17,6 +17,7 @@ let ReactNoopFlightServer; let ReactNoopFlightClient; let ErrorBoundary; let NoErrorExpected; +let Scheduler; describe('ReactFlight', () => { beforeEach(() => { @@ -27,6 +28,7 @@ describe('ReactFlight', () => { ReactNoopFlightServer = require('react-noop-renderer/flight-server'); ReactNoopFlightClient = require('react-noop-renderer/flight-client'); act = require('jest-react').act; + Scheduler = require('scheduler'); ErrorBoundary = class extends React.Component { state = {hasError: false, error: null}; @@ -302,4 +304,200 @@ describe('ReactFlight', () => { {withoutStack: true}, ); }); + + it('supports basic createServerContext usage', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'hello from server', + ); + function Foo() { + const context = React.useServerContext(ServerContext); + return
{context}
; + } + + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(
hello from server
); + }); + + it('propagates ServerContext providers in flight', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + + function Foo() { + return ( +
+ + + +
+ ); + } + function Bar() { + const context = React.useServerContext(ServerContext); + return context; + } + + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); + }); + + it('propagates ServerContext and cleansup providers in flight', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + + function Foo() { + return ( + <> + + + + + + + + + + + + + + + ); + } + function Bar() { + const context = React.useServerContext(ServerContext); + return {context}; + } + + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput( + <> + hi this is server + hi this is server2 + hi this is server outer + hi this is server outer2 + default hello from server + , + ); + }); + + it('propagates ServerContext providers in flight after suspending', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + + function Foo() { + return ( +
+ + + + + +
+ ); + } + + let resolve; + const promise = new Promise(res => { + resolve = () => { + promise.unsuspend = true; + res(); + }; + }); + + function Bar() { + if (!promise.unsuspend) { + Scheduler.unstable_yieldValue('suspended'); + throw promise; + } + Scheduler.unstable_yieldValue('rendered'); + const context = React.useServerContext(ServerContext); + return context; + } + + const transport = ReactNoopFlightServer.render(); + + expect(Scheduler).toHaveYielded(['suspended']); + + await act(async () => { + resolve(); + await promise; + jest.runAllImmediates(); + }); + + expect(Scheduler).toHaveYielded(['rendered']); + + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); + }); + + it('serializes ServerContext to client', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + + function ClientBar() { + Scheduler.unstable_yieldValue('ClientBar'); + const context = React.useServerContext(ServerContext); + return {context}; + } + + const Bar = moduleReference(ClientBar); + + function Foo() { + return ( + + + + ); + } + + const model = { + foo: , + }; + + const transport = ReactNoopFlightServer.render(model); + + expect(Scheduler).toHaveYielded([]); + + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + const flightModel = ReactNoopFlightClient.read(transport); + ReactNoop.render(flightModel.foo); + }); + + expect(Scheduler).toHaveYielded(['ClientBar']); + expect(ReactNoop).toMatchRenderedOutput(hi this is server); + }); }); diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index b7159859060..5ebca44b925 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -12,8 +12,10 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, ReactProviderType, StartTransitionOptions, + ServerContextJSONValue, } from 'shared/ReactTypes'; import type { Fiber, @@ -105,7 +107,9 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext(context: ReactContext): T { +function readContext( + context: ReactContext | ReactServerContext, +): T { // For now we don't expose readContext usage in the hooks debugging info. return context._currentValue; } @@ -119,6 +123,17 @@ function useContext(context: ReactContext): T { return context._currentValue; } +function useServerContext( + context: ReactServerContext, +): T { + hookLog.push({ + primitive: 'ServerContext', + stackError: new Error(), + value: context._currentValue, + }); + return context._currentValue; +} + function useState( initialState: (() => S) | S, ): [S, Dispatch>] { @@ -348,6 +363,7 @@ const Dispatcher: DispatcherType = { useMemo, useReducer, useRef, + useServerContext, useState, useTransition, useMutableSource, diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index 4d975dbfec0..c1183a36c06 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -83,9 +83,9 @@ export type GetFiberIDForNative = ( ) => number | null; export type FindNativeNodesForFiberID = (id: number) => ?Array; -export type ReactProviderType = { +export type ReactProviderType = { $$typeof: Symbol | number, - _context: ReactContext, + _context: ReactContext | ReactServerContext, ... }; diff --git a/packages/react-dom/src/server/ReactPartialRendererContext.js b/packages/react-dom/src/server/ReactPartialRendererContext.js index 65af591e365..bdcd9f4ba65 100644 --- a/packages/react-dom/src/server/ReactPartialRendererContext.js +++ b/packages/react-dom/src/server/ReactPartialRendererContext.js @@ -8,7 +8,7 @@ */ import type {ThreadID} from './ReactThreadIDAllocator'; -import type {ReactContext} from 'shared/ReactTypes'; +import type {ReactContext, ReactServerContext} from 'shared/ReactTypes'; import {disableLegacyContext} from 'shared/ReactFeatureFlags'; import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols'; @@ -44,7 +44,7 @@ function checkContextTypes(typeSpecs, values, location: string) { } export function validateContextBounds( - context: ReactContext, + context: ReactContext | ReactServerContext, threadID: ThreadID, ) { // If we don't have enough slots in this context to store this threadID, diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 2940c47bde4..1da0127a131 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -14,6 +14,8 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, + ServerContextValue, } from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; @@ -222,7 +224,9 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext(context: ReactContext): T { +function readContext( + context: ReactContext | ReactServerContext, +): T { const threadID = currentPartialRenderer.threadID; validateContextBounds(context, threadID); if (__DEV__) { @@ -248,6 +252,18 @@ function useContext(context: ReactContext): T { return context[threadID]; } +function useServerContext( + context: ReactServerContext, +): T { + if (__DEV__) { + currentHookNameInDev = 'useContext'; + } + resolveCurrentlyRenderingComponent(); + const threadID = currentPartialRenderer.threadID; + validateContextBounds(context, threadID); + return context[threadID]; +} + function basicStateReducer(state: S, action: BasicStateAction): S { // $FlowFixMe: Flow doesn't like mixed types return typeof action === 'function' ? action(state) : action; @@ -531,6 +547,7 @@ export const Dispatcher: DispatcherType = { useReducer, useRef, useState, + useServerContext, useInsertionEffect, useLayoutEffect, useCallback, diff --git a/packages/react-noop-renderer/src/ReactNoopFlightClient.js b/packages/react-noop-renderer/src/ReactNoopFlightClient.js index df586c6efb2..b4e2e830fc8 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightClient.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightClient.js @@ -40,7 +40,8 @@ function read(source: Source): T { processStringChunk(response, source[i], 0); } close(response); - return response.readRoot(); + const root = response.readRoot(); + return root; } export {read}; diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index ca6fedf2ec6..5ef26f57813 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -27,8 +27,9 @@ const ReactNoopFlightServer = ReactFlightServer({ callback(); }, beginWriting(destination: Destination): void {}, - writeChunk(destination: Destination, chunk: string): void { + writeChunk(destination: Destination, chunk: string): boolean { destination.push(chunk); + return true; }, writeChunkAndReturn(destination: Destination, chunk: string): boolean { destination.push(chunk); @@ -58,8 +59,16 @@ const ReactNoopFlightServer = ReactFlightServer({ }, }); +type ServerContextJSONValue = + | string + | boolean + | number + | null + | $ReadOnlyArray; + type Options = { onError?: (error: mixed) => void, + context?: Array<{name: string, value: ServerContextJSONValue}>, }; function render(model: ReactModel, options?: Options): Destination { @@ -68,7 +77,7 @@ function render(model: ReactModel, options?: Options): Destination { const request = ReactNoopFlightServer.createRequest( model, bundlerConfig, - options ? options.onError : undefined, + options, ); ReactNoopFlightServer.startWork(request); ReactNoopFlightServer.startFlowing(request, destination); diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 09a0e16a1e5..fef0efef078 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactProviderType, ReactContext} from 'shared/ReactTypes'; +import type { + ReactProviderType, + ReactContext, + ReactServerContext, +} from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {TypeOfMode} from './ReactTypeOfMode'; @@ -3217,7 +3221,8 @@ function updateContextProvider( renderLanes: Lanes, ) { const providerType: ReactProviderType = workInProgress.type; - const context: ReactContext = providerType._context; + const context: ReactContext | ReactServerContext = + providerType._context; const newProps = workInProgress.pendingProps; const oldProps = workInProgress.memoizedProps; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 2db03f39a2c..ab0b17caafb 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactProviderType, ReactContext} from 'shared/ReactTypes'; +import type { + ReactProviderType, + ReactContext, + ReactServerContext, +} from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {TypeOfMode} from './ReactTypeOfMode'; @@ -3217,7 +3221,8 @@ function updateContextProvider( renderLanes: Lanes, ) { const providerType: ReactProviderType = workInProgress.type; - const context: ReactContext = providerType._context; + const context: ReactContext | ReactServerContext = + providerType._context; const newProps = workInProgress.pendingProps; const oldProps = workInProgress.memoizedProps; diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 011c64f59d2..697423c36d9 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -13,6 +13,8 @@ import type { MutableSourceSubscribeFn, ReactContext, StartTransitionOptions, + ReactServerContext, + ServerContextJSONValue, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.new'; @@ -2401,6 +2403,7 @@ export const ContextOnlyDispatcher: Dispatcher = { useCallback: throwInvalidHookError, useContext: throwInvalidHookError, + useServerContext: throwInvalidHookError, useEffect: throwInvalidHookError, useImperativeHandle: throwInvalidHookError, useInsertionEffect: throwInvalidHookError, @@ -2429,6 +2432,7 @@ const HooksDispatcherOnMount: Dispatcher = { useCallback: mountCallback, useContext: readContext, + useServerContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, useLayoutEffect: mountLayoutEffect, @@ -2457,6 +2461,7 @@ const HooksDispatcherOnUpdate: Dispatcher = { useCallback: updateCallback, useContext: readContext, + useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2485,6 +2490,7 @@ const HooksDispatcherOnRerender: Dispatcher = { useCallback: updateCallback, useContext: readContext, + useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2536,7 +2542,7 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2550,6 +2556,13 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + mountHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2684,7 +2697,7 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2697,6 +2710,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2826,7 +2846,7 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2839,6 +2859,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2968,7 +2995,7 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, @@ -2982,6 +3009,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3111,7 +3145,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3127,6 +3161,14 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3270,7 +3312,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3286,6 +3328,14 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3446,6 +3496,14 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 48f8f14bf2e..0869742c8c5 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -13,6 +13,8 @@ import type { MutableSourceSubscribeFn, ReactContext, StartTransitionOptions, + ReactServerContext, + ServerContextJSONValue, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.old'; @@ -2401,6 +2403,7 @@ export const ContextOnlyDispatcher: Dispatcher = { useCallback: throwInvalidHookError, useContext: throwInvalidHookError, + useServerContext: throwInvalidHookError, useEffect: throwInvalidHookError, useImperativeHandle: throwInvalidHookError, useInsertionEffect: throwInvalidHookError, @@ -2429,6 +2432,7 @@ const HooksDispatcherOnMount: Dispatcher = { useCallback: mountCallback, useContext: readContext, + useServerContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, useLayoutEffect: mountLayoutEffect, @@ -2457,6 +2461,7 @@ const HooksDispatcherOnUpdate: Dispatcher = { useCallback: updateCallback, useContext: readContext, + useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2485,6 +2490,7 @@ const HooksDispatcherOnRerender: Dispatcher = { useCallback: updateCallback, useContext: readContext, + useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2536,7 +2542,7 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2550,6 +2556,13 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + mountHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2684,7 +2697,7 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2697,6 +2710,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2826,7 +2846,7 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2839,6 +2859,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2968,7 +2995,7 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { return readContext(context); }, @@ -2982,6 +3009,13 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3111,7 +3145,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3127,6 +3161,14 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3270,7 +3312,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3286,6 +3328,14 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3446,6 +3496,14 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, + useServerContext( + context: ReactServerContext, + ): T { + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }, useEffect( create: () => (() => void) | void, deps: Array | void | null, diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 8ff30c810f0..e73158d2f77 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; +import type { + ReactContext, + ReactServerContext, + ReactProviderType, +} from 'shared/ReactTypes'; import type { Fiber, ContextDependency, @@ -83,11 +87,12 @@ export function exitDisallowedContextReadInDEV(): void { } } -export function pushProvider( +export function pushProvider( providerFiber: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, nextValue: T, ): void { + debugger; if (isPrimaryRenderer) { push(valueCursor, context._currentValue, providerFiber); @@ -125,16 +130,16 @@ export function pushProvider( } } -export function popProvider( - context: ReactContext, +export function popProvider( + context: ReactContext | ReactServerContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = currentValue; + context._currentValue = (currentValue: any); } else { - context._currentValue2 = currentValue; + context._currentValue2 = (currentValue: any); } } @@ -180,9 +185,9 @@ export function scheduleContextWorkOnParentPath( } } -export function propagateContextChange( +export function propagateContextChange( workInProgress: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, renderLanes: Lanes, ): void { if (enableLazyContextPropagation) { @@ -201,9 +206,9 @@ export function propagateContextChange( } } -function propagateContextChange_eager( +function propagateContextChange_eager( workInProgress: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, renderLanes: Lanes, ): void { // Only used by eager implementation @@ -341,7 +346,7 @@ function propagateContextChange_eager( } } -function propagateContextChanges( +function propagateContextChanges( workInProgress: Fiber, contexts: Array, renderLanes: Lanes, @@ -370,7 +375,7 @@ function propagateContextChanges( const dependency = dep; const consumer = fiber; findContext: for (let i = 0; i < contexts.length; i++) { - const context: ReactContext = contexts[i]; + const context: ReactContext | ReactServerContext = contexts[i]; // Check if the context matches. // TODO: Compare selected values to bail out early. if (dependency.context === context) { @@ -537,7 +542,8 @@ function propagateParentContextChanges( const oldProps = currentParent.memoizedProps; if (oldProps !== null) { const providerType: ReactProviderType = parent.type; - const context: ReactContext = providerType._context; + const context: ReactContext | ReactServerContext = + providerType._context; const newProps = parent.pendingProps; const newValue = newProps.value; @@ -640,7 +646,9 @@ export function prepareToReadContext( } } -export function readContext(context: ReactContext): T { +export function readContext( + context: ReactContext | ReactServerContext, +): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 93fe3bc8395..0adc1d03c6b 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; +import type { + ReactContext, + ReactServerContext, + ReactProviderType, +} from 'shared/ReactTypes'; import type { Fiber, ContextDependency, @@ -83,9 +87,9 @@ export function exitDisallowedContextReadInDEV(): void { } } -export function pushProvider( +export function pushProvider( providerFiber: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, nextValue: T, ): void { if (isPrimaryRenderer) { @@ -125,16 +129,16 @@ export function pushProvider( } } -export function popProvider( - context: ReactContext, +export function popProvider( + context: ReactContext | ReactServerContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = currentValue; + context._currentValue = (currentValue: any); } else { - context._currentValue2 = currentValue; + context._currentValue2 = (currentValue: any); } } @@ -180,9 +184,9 @@ export function scheduleContextWorkOnParentPath( } } -export function propagateContextChange( +export function propagateContextChange( workInProgress: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, renderLanes: Lanes, ): void { if (enableLazyContextPropagation) { @@ -201,9 +205,9 @@ export function propagateContextChange( } } -function propagateContextChange_eager( +function propagateContextChange_eager( workInProgress: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, renderLanes: Lanes, ): void { // Only used by eager implementation @@ -341,7 +345,7 @@ function propagateContextChange_eager( } } -function propagateContextChanges( +function propagateContextChanges( workInProgress: Fiber, contexts: Array, renderLanes: Lanes, @@ -370,7 +374,7 @@ function propagateContextChanges( const dependency = dep; const consumer = fiber; findContext: for (let i = 0; i < contexts.length; i++) { - const context: ReactContext = contexts[i]; + const context: ReactContext | ReactServerContext = contexts[i]; // Check if the context matches. // TODO: Compare selected values to bail out early. if (dependency.context === context) { @@ -537,7 +541,8 @@ function propagateParentContextChanges( const oldProps = currentParent.memoizedProps; if (oldProps !== null) { const providerType: ReactProviderType = parent.type; - const context: ReactContext = providerType._context; + const context: ReactContext | ReactServerContext = + providerType._context; const newProps = parent.pendingProps; const newValue = newProps.value; @@ -640,7 +645,9 @@ export function prepareToReadContext( } } -export function readContext(context: ReactContext): T { +export function readContext( + context: ReactContext | ReactServerContext, +): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. diff --git a/packages/react-reconciler/src/ReactFiberScope.new.js b/packages/react-reconciler/src/ReactFiberScope.new.js index ccc589a9dfe..491e82d89d3 100644 --- a/packages/react-reconciler/src/ReactFiberScope.new.js +++ b/packages/react-reconciler/src/ReactFiberScope.new.js @@ -12,6 +12,7 @@ import type { ReactScopeInstance, ReactContext, ReactScopeQuery, + ReactServerContext, } from 'shared/ReactTypes'; import { @@ -108,9 +109,9 @@ function collectFirstScopedNodeFromChildren( return null; } -function collectNearestContextValues( +function collectNearestContextValues( node: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, childContextValues: Array, ): void { if (node.tag === ContextProvider && node.type._context === context) { @@ -128,9 +129,9 @@ function collectNearestContextValues( } } -function collectNearestChildContextValues( +function collectNearestChildContextValues( startingChild: Fiber | null, - context: ReactContext, + context: ReactContext | ReactServerContext, childContextValues: Array, ): void { let child = startingChild; @@ -176,7 +177,9 @@ function containsNode(node: Object): boolean { return false; } -function getChildContextValues(context: ReactContext): Array { +function getChildContextValues( + context: ReactContext | ReactServerContext, +): Array { const currentFiber = getInstanceFromScope(this); if (currentFiber === null) { return []; diff --git a/packages/react-reconciler/src/ReactFiberScope.old.js b/packages/react-reconciler/src/ReactFiberScope.old.js index ccc589a9dfe..491e82d89d3 100644 --- a/packages/react-reconciler/src/ReactFiberScope.old.js +++ b/packages/react-reconciler/src/ReactFiberScope.old.js @@ -12,6 +12,7 @@ import type { ReactScopeInstance, ReactContext, ReactScopeQuery, + ReactServerContext, } from 'shared/ReactTypes'; import { @@ -108,9 +109,9 @@ function collectFirstScopedNodeFromChildren( return null; } -function collectNearestContextValues( +function collectNearestContextValues( node: Fiber, - context: ReactContext, + context: ReactContext | ReactServerContext, childContextValues: Array, ): void { if (node.tag === ContextProvider && node.type._context === context) { @@ -128,9 +129,9 @@ function collectNearestContextValues( } } -function collectNearestChildContextValues( +function collectNearestChildContextValues( startingChild: Fiber | null, - context: ReactContext, + context: ReactContext | ReactServerContext, childContextValues: Array, ): void { let child = startingChild; @@ -176,7 +177,9 @@ function containsNode(node: Object): boolean { return false; } -function getChildContextValues(context: ReactContext): Array { +function getChildContextValues( + context: ReactContext | ReactServerContext, +): Array { const currentFiber = getInstanceFromScope(this); if (currentFiber === null) { return []; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 7223ad7d052..1516817638c 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -1555,7 +1555,6 @@ function handleError(root, thrownValue): void { ); } } - throwException( root, erroredWork.return, diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 162ba457d54..52a96dfc96d 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -11,11 +11,14 @@ import type {Source} from 'shared/ReactElementType'; import type { RefObject, ReactContext, + ReactServerContext, + ServerContextJSONValue, MutableSourceSubscribeFn, MutableSourceGetSnapshotFn, MutableSourceVersion, MutableSource, StartTransitionOptions, + Wakeable, } from 'shared/ReactTypes'; import type {SuspenseInstance} from './ReactFiberHostConfig'; import type {WorkTag} from './ReactWorkTags'; @@ -24,7 +27,6 @@ import type {Flags} from './ReactFiberFlags'; import type {Lane, Lanes, LaneMap} from './ReactFiberLane.old'; import type {RootTag} from './ReactRootTags'; import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig'; -import type {Wakeable} from 'shared/ReactTypes'; import type {Cache} from './ReactFiberCacheComponent.old'; import type {Transitions} from './ReactFiberTracingMarkerComponent.new'; @@ -46,10 +48,11 @@ export type HookType = | 'useMutableSource' | 'useSyncExternalStore' | 'useId' - | 'useCacheRefresh'; + | 'useCacheRefresh' + | 'useServerContext'; -export type ContextDependency = { - context: ReactContext, +export type ContextDependency = { + context: ReactContext | ReactServerContext, next: ContextDependency | null, memoizedValue: T, ... @@ -342,7 +345,7 @@ type Dispatch
= A => void; export type Dispatcher = {| getCacheSignal?: () => AbortSignal, getCacheForType?: (resourceType: () => T) => T, - readContext(context: ReactContext): T, + readContext(context: ReactContext | ReactServerContext): T, useState(initialState: (() => S) | S): [S, Dispatch>], useReducer( reducer: (S, A) => S, @@ -381,6 +384,9 @@ export type Dispatcher = {| getSnapshot: MutableSourceGetSnapshotFn, subscribe: MutableSourceSubscribeFn, ): Snapshot, + useServerContext( + context: ReactServerContext, + ): T, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js index 971eca0908c..b891c7df00a 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js @@ -75,45 +75,12 @@ export function processErrorChunk( ]; } -function convertModelToJSON( - request: Request, - parent: {+[key: string]: ReactModel} | $ReadOnlyArray, - key: string, - model: ReactModel, -): JSONValue { - const json = resolveModelToJSON(request, parent, key, model); - if (typeof json === 'object' && json !== null) { - if (isArray(json)) { - const jsonArray: Array = []; - for (let i = 0; i < json.length; i++) { - jsonArray[i] = convertModelToJSON(request, json, '' + i, json[i]); - } - return jsonArray; - } else { - const jsonObj: {[key: string]: JSONValue} = {}; - for (const nextKey in json) { - if (hasOwnProperty.call(json, nextKey)) { - jsonObj[nextKey] = convertModelToJSON( - request, - json, - nextKey, - json[nextKey], - ); - } - } - return jsonObj; - } - } - return json; -} - export function processModelChunk( request: Request, id: number, - model: ReactModel, + jsonValue: JSONValue, ): Chunk { - const json = convertModelToJSON(request, {}, '', model); - return ['J', id, json]; + return ['J', id, jsonValue]; } export function processModuleChunk( diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js index 35518c9d33b..4b6a8b2856c 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js @@ -7,7 +7,10 @@ * @flow */ -import type {ReactModel} from 'react-server/src/ReactFlightServer'; +import type { + ReactModel, + RequestOptions, +} from 'react-server/src/ReactFlightServer'; import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig'; import { @@ -16,20 +19,12 @@ import { startFlowing, } from 'react-server/src/ReactFlightServer'; -type Options = { - onError?: (error: mixed) => void, -}; - function renderToReadableStream( model: ReactModel, webpackMap: BundlerConfig, - options?: Options, + options?: RequestOptions, ): ReadableStream { - const request = createRequest( - model, - webpackMap, - options ? options.onError : undefined, - ); + const request = createRequest(model, webpackMap, options); const stream = new ReadableStream({ type: 'bytes', start(controller) { diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js index 5f992d4b03e..92abb8afae1 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js @@ -7,7 +7,10 @@ * @flow */ -import type {ReactModel} from 'react-server/src/ReactFlightServer'; +import type { + ReactModel, + RequestOptions, +} from 'react-server/src/ReactFlightServer'; import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig'; import type {Writable} from 'stream'; @@ -21,10 +24,6 @@ function createDrainHandler(destination, request) { return () => startFlowing(request, destination); } -type Options = { - onError?: (error: mixed) => void, -}; - type Controls = {| pipe(destination: T): T, |}; @@ -32,13 +31,9 @@ type Controls = {| function renderToPipeableStream( model: ReactModel, webpackMap: BundlerConfig, - options?: Options, + options?: RequestOptions, ): Controls { - const request = createRequest( - model, - webpackMap, - options ? options.onError : undefined, - ); + const request = createRequest(model, webpackMap, options); let hasStartedFlowing = false; startWork(request); return { diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js index 0387d94ecad..c6e735a3350 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js @@ -107,10 +107,9 @@ function convertModelToJSON( export function processModelChunk( request: Request, id: number, - model: ReactModel, + jsonValue: JSONValue, ): Chunk { - const json = convertModelToJSON(request, {}, '', model); - return ['J', id, json]; + return ['J', id, jsonValue]; } export function processModuleChunk( diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index c3ffa8cd6ab..170ac083b60 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -15,6 +15,8 @@ import type { MutableSourceSubscribeFn, ReactContext, StartTransitionOptions, + ReactServerContext, + ServerContextJSONValue, } from 'shared/ReactTypes'; import type {ResponseState} from './ReactServerFormatConfig'; @@ -243,7 +245,9 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext(context: ReactContext): T { +function readContext( + context: ReactContext | ReactServerContext, +): T { if (__DEV__) { if (isInHookUserCodeInDev) { console.error( @@ -265,6 +269,16 @@ function useContext(context: ReactContext): T { return readContextImpl(context); } +function useServerContext( + context: ReactServerContext, +): T { + if (__DEV__) { + currentHookNameInDev = 'useServerContext'; + } + resolveCurrentlyRenderingComponent(); + return readContextImpl(context); +} + function basicStateReducer(state: S, action: BasicStateAction): S { // $FlowFixMe: Flow doesn't like mixed types return typeof action === 'function' ? action(state) : action; @@ -542,6 +556,7 @@ function noop(): void {} export const Dispatcher: DispatcherType = { readContext, useContext, + useServerContext, useMemo, useReducer, useRef, diff --git a/packages/react-server/src/ReactFizzNewContext.js b/packages/react-server/src/ReactFizzNewContext.js index 0eaa07f839a..01df3d84494 100644 --- a/packages/react-server/src/ReactFizzNewContext.js +++ b/packages/react-server/src/ReactFizzNewContext.js @@ -7,7 +7,7 @@ * @flow */ -import type {ReactContext} from 'shared/ReactTypes'; +import type {ReactContext, ReactServerContext} from 'shared/ReactTypes'; import {isPrimaryRenderer} from './ReactServerFormatConfig'; @@ -19,10 +19,10 @@ if (__DEV__) { // Used to store the parent path of all context overrides in a shared linked list. // Forming a reverse tree. -type ContextNode = { +type ContextNode = { parent: null | ContextNode, depth: number, // Short hand to compute the depth of the tree at this node. - context: ReactContext, + context: ReactContext | ReactServerContext, parentValue: T, value: T, }; @@ -177,8 +177,8 @@ export function switchContext(newSnapshot: ContextSnapshot): void { } } -export function pushProvider( - context: ReactContext, +export function pushProvider( + context: ReactContext | ReactServerContext, nextValue: T, ): ContextSnapshot { let prevValue; @@ -227,7 +227,9 @@ export function pushProvider( return newNode; } -export function popProvider(context: ReactContext): ContextSnapshot { +export function popProvider( + context: ReactContext | ReactServerContext, +): ContextSnapshot { const prevSnapshot = currentActiveSnapshot; if (prevSnapshot === null) { @@ -281,7 +283,9 @@ export function getActiveContext(): ContextSnapshot { return currentActiveSnapshot; } -export function readContext(context: ReactContext): T { +export function readContext( + context: ReactContext | ReactServerContext, +): T { const value = isPrimaryRenderer ? context._currentValue : context._currentValue2; diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js new file mode 100644 index 00000000000..36b37eb4a5d --- /dev/null +++ b/packages/react-server/src/ReactFlightHooks.js @@ -0,0 +1,113 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes'; +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; +import {readContext as readContextImpl} from './ReactFlightNewContext'; + +function readContext( + context: ReactServerContext, +): T { + if (__DEV__) { + if (currentCache === null) { + console.error( + 'Context can only be read while React is rendering. ' + + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + + 'In function components, you can read it directly in the function body, but not ' + + 'inside Hooks like useReducer() or useMemo().', + ); + } + } + return readContextImpl(context); +} + +export const Dispatcher: DispatcherType = { + useMemo(nextCreate: () => T): T { + return nextCreate(); + }, + useCallback(callback: T): T { + return callback; + }, + useDebugValue(): void {}, + useDeferredValue: (unsupportedHook: any), + useTransition: (unsupportedHook: any), + getCacheForType(resourceType: () => T): T { + if (!currentCache) { + throw new Error('Reading the cache is only supported while rendering.'); + } + + let entry: T | void = (currentCache.get(resourceType): any); + if (entry === undefined) { + entry = resourceType(); + // TODO: Warn if undefined? + currentCache.set(resourceType, entry); + } + return entry; + }, + readContext, + useContext: (unsupportedHook: any), + useReducer: (unsupportedHook: any), + useRef: (unsupportedHook: any), + useState: (unsupportedHook: any), + useInsertionEffect: (unsupportedHook: any), + useLayoutEffect: (unsupportedHook: any), + useImperativeHandle: (unsupportedHook: any), + useEffect: (unsupportedHook: any), + useId: (unsupportedHook: any), + useMutableSource: (unsupportedHook: any), + useServerContext: function useServerContext( + context: ReactServerContext, + ): T { + if (!currentCache) { + throw new Error('useServerContext is only supported while rendering.'); + } + return readContextImpl(context); + }, + useSyncExternalStore: (unsupportedHook: any), + useCacheRefresh(): (?() => T, ?T) => void { + return unsupportedRefresh; + }, +}; + +function unsupportedHook(): void { + throw new Error('This Hook is not supported in Server Components.'); +} + +function unsupportedRefresh(): void { + if (!currentCache) { + throw new Error( + 'Refreshing the cache is not supported in Server Components.', + ); + } +} + +let currentCache: Map | null = null; + +export function setCurrentCache(cache: Map | null) { + currentCache = cache; + return currentCache; +} + +export function getCurrentCache() { + return currentCache; +} + +type ServerContextCache = {[name: string]: ReactServerContext} | null; +let currentServerContexts: ServerContextCache = null; + +export function setCurrentServerContexts(contexts: ServerContextCache) { + currentServerContexts = contexts; +} + +export function getCurrentServerContexts(): ServerContextCache { + return currentServerContexts; +} diff --git a/packages/react-server/src/ReactFlightNewContext.js b/packages/react-server/src/ReactFlightNewContext.js new file mode 100644 index 00000000000..b3c52c71157 --- /dev/null +++ b/packages/react-server/src/ReactFlightNewContext.js @@ -0,0 +1,246 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; + +let rendererSigil; +if (__DEV__) { + // Use this to detect multiple renderers using the same context + rendererSigil = {}; +} + +// Used to store the parent path of all context overrides in a shared linked list. +// Forming a reverse tree. +type ContextNode = { + parent: null | ContextNode, + depth: number, // Short hand to compute the depth of the tree at this node. + context: ReactServerContext, + parentValue: T, + value: T, +}; + +// The structure of a context snapshot is an implementation of this file. +// Currently, it's implemented as tracking the current active node. +export opaque type ContextSnapshot = null | ContextNode; + +export const rootContextSnapshot: ContextSnapshot = null; + +// We assume that this runtime owns the "current" field on all ReactContext instances. +// This global (actually thread local) state represents what state all those "current", +// fields are currently in. +let currentActiveSnapshot: ContextSnapshot = null; + +function popNode(prev: ContextNode): void { + prev.context._currentValue = prev.parentValue; +} + +function pushNode(next: ContextNode): void { + next.context._currentValue = next.value; +} + +function popToNearestCommonAncestor( + prev: ContextNode, + next: ContextNode, +): void { + if (prev === next) { + // We've found a shared ancestor. We don't need to pop nor reapply this one or anything above. + } else { + popNode(prev); + const parentPrev = prev.parent; + const parentNext = next.parent; + if (parentPrev === null) { + if (parentNext !== null) { + throw new Error( + 'The stacks must reach the root at the same time. This is a bug in React.', + ); + } + } else { + if (parentNext === null) { + throw new Error( + 'The stacks must reach the root at the same time. This is a bug in React.', + ); + } + + popToNearestCommonAncestor(parentPrev, parentNext); + // On the way back, we push the new ones that weren't common. + pushNode(next); + } + } +} + +function popAllPrevious(prev: ContextNode): void { + popNode(prev); + const parentPrev = prev.parent; + if (parentPrev !== null) { + popAllPrevious(parentPrev); + } +} + +function pushAllNext(next: ContextNode): void { + const parentNext = next.parent; + if (parentNext !== null) { + pushAllNext(parentNext); + } + pushNode(next); +} + +function popPreviousToCommonLevel( + prev: ContextNode, + next: ContextNode, +): void { + popNode(prev); + const parentPrev = prev.parent; + + if (parentPrev === null) { + throw new Error( + 'The depth must equal at least at zero before reaching the root. This is a bug in React.', + ); + } + + if (parentPrev.depth === next.depth) { + // We found the same level. Now we just need to find a shared ancestor. + popToNearestCommonAncestor(parentPrev, next); + } else { + // We must still be deeper. + popPreviousToCommonLevel(parentPrev, next); + } +} + +function popNextToCommonLevel( + prev: ContextNode, + next: ContextNode, +): void { + const parentNext = next.parent; + + if (parentNext === null) { + throw new Error( + 'The depth must equal at least at zero before reaching the root. This is a bug in React.', + ); + } + + if (prev.depth === parentNext.depth) { + // We found the same level. Now we just need to find a shared ancestor. + popToNearestCommonAncestor(prev, parentNext); + } else { + // We must still be deeper. + popNextToCommonLevel(prev, parentNext); + } + pushNode(next); +} + +// Perform context switching to the new snapshot. +// To make it cheap to read many contexts, while not suspending, we make the switch eagerly by +// updating all the context's current values. That way reads, always just read the current value. +// At the cost of updating contexts even if they're never read by this subtree. +export function switchContext(newSnapshot: ContextSnapshot): void { + // The basic algorithm we need to do is to pop back any contexts that are no longer on the stack. + // We also need to update any new contexts that are now on the stack with the deepest value. + // The easiest way to update new contexts is to just reapply them in reverse order from the + // perspective of the backpointers. To avoid allocating a lot when switching, we use the stack + // for that. Therefore this algorithm is recursive. + // 1) First we pop which ever snapshot tree was deepest. Popping old contexts as we go. + // 2) Then we find the nearest common ancestor from there. Popping old contexts as we go. + // 3) Then we reapply new contexts on the way back up the stack. + const prev = currentActiveSnapshot; + const next = newSnapshot; + if (prev !== next) { + if (prev === null) { + // $FlowFixMe: This has to be non-null since it's not equal to prev. + pushAllNext(next); + } else if (next === null) { + popAllPrevious(prev); + } else if (prev.depth === next.depth) { + popToNearestCommonAncestor(prev, next); + } else if (prev.depth > next.depth) { + popPreviousToCommonLevel(prev, next); + } else { + popNextToCommonLevel(prev, next); + } + currentActiveSnapshot = next; + } +} + +export function pushProvider( + context: ReactServerContext, + nextValue: T, +): ContextSnapshot { + const prevValue = context._currentValue; + context._currentValue = nextValue; + if (__DEV__) { + if ( + context._currentRenderer !== undefined && + context._currentRenderer !== null && + context._currentRenderer !== rendererSigil + ) { + console.error( + 'Detected multiple renderers concurrently rendering the ' + + 'same context provider. This is currently unsupported.', + ); + } + context._currentRenderer = rendererSigil; + } + const prevNode = currentActiveSnapshot; + const newNode: ContextNode = { + parent: prevNode, + depth: prevNode === null ? 0 : prevNode.depth + 1, + context: context, + parentValue: prevValue, + value: nextValue, + }; + currentActiveSnapshot = newNode; + return newNode; +} + +export function popProvider( + context: ReactServerContext, +): ContextSnapshot { + const prevSnapshot = currentActiveSnapshot; + + if (prevSnapshot === null) { + throw new Error( + 'Tried to pop a Context at the root of the app. This is a bug in React.', + ); + } + + if (__DEV__) { + if (prevSnapshot.context !== context) { + console.error( + 'The parent context is not the expected context. This is probably a bug in React.', + ); + } + } + prevSnapshot.context._currentValue = prevSnapshot.parentValue; + if (__DEV__) { + if ( + context._currentRenderer !== undefined && + context._currentRenderer !== null && + context._currentRenderer !== rendererSigil + ) { + console.error( + 'Detected multiple renderers concurrently rendering the ' + + 'same context provider. This is currently unsupported.', + ); + } + context._currentRenderer = rendererSigil; + } + return (currentActiveSnapshot = prevSnapshot.parent); +} + +export function getActiveContext(): ContextSnapshot { + return currentActiveSnapshot; +} + +export function readContext( + context: ReactServerContext, +): T { + return context._currentValue; +} diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 2b0e7304af1..3324be545d1 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -7,7 +7,6 @@ * @flow */ -import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes'; import type { Destination, Chunk, @@ -16,6 +15,11 @@ import type { ModuleReference, ModuleKey, } from './ReactFlightServerConfig'; +import type {ContextSnapshot} from './ReactFlightNewContext'; +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; import { scheduleWork, @@ -34,12 +38,28 @@ import { isModuleReference, } from './ReactFlightServerConfig'; +import {createServerContext} from 'react/src/ReactServerContext'; +import { + Dispatcher, + getCurrentCache, + setCurrentCache, + setCurrentServerContexts, +} from './ReactFlightHooks'; +import { + pushProvider, + popProvider, + switchContext, + getActiveContext, + rootContextSnapshot, +} from './ReactFlightNewContext'; + import { REACT_ELEMENT_TYPE, REACT_FORWARD_REF_TYPE, REACT_FRAGMENT_TYPE, REACT_LAZY_TYPE, REACT_MEMO_TYPE, + REACT_PROVIDER_TYPE, } from 'shared/ReactSymbols'; import ReactSharedInternals from 'shared/ReactSharedInternals'; @@ -68,6 +88,7 @@ type Segment = { id: number, model: ReactModel, ping: () => void, + context: ContextSnapshot, }; export type Request = { @@ -88,6 +109,11 @@ export type Request = { toJSON: (key: string, value: ReactModel) => ReactJSONValue, }; +export type RequestOptions = { + onError?: (error: mixed) => void, + context?: Array<{name: string, value: ServerContextJSONValue}>, +}; + const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; function defaultErrorHandler(error: mixed) { @@ -102,9 +128,10 @@ const CLOSED = 2; export function createRequest( model: ReactModel, bundlerConfig: BundlerConfig, - onError: void | ((error: mixed) => void), + options?: RequestOptions, ): Request { const pingedSegments = []; + const onError = options?.onError; const request = { status: OPEN, fatalError: null, @@ -125,11 +152,18 @@ export function createRequest( }, }; request.pendingChunks++; - const rootSegment = createSegment(request, model); + const context = createRootContext(options?.context); + const rootSegment = createSegment(request, model, context); pingedSegments.push(rootSegment); return request; } +function createRootContext( + reqContext?: Array<{name: string, value: ServerContextJSONValue}>, +) { + return importServerContexts(reqContext); +} + function attemptResolveElement( type: any, key: null | React$Key, @@ -174,6 +208,9 @@ function attemptResolveElement( case REACT_MEMO_TYPE: { return attemptResolveElement(type.type, key, ref, props); } + case REACT_PROVIDER_TYPE: { + return [REACT_PROVIDER_TYPE, type._context.displayName, key, props]; + } } } throw new Error( @@ -189,11 +226,16 @@ function pingSegment(request: Request, segment: Segment): void { } } -function createSegment(request: Request, model: ReactModel): Segment { +function createSegment( + request: Request, + model: ReactModel, + context: ContextSnapshot, +): Segment { const id = request.nextChunkId++; const segment = { id, model, + context, ping: () => pingSegment(request, segment), }; return segment; @@ -207,6 +249,10 @@ function serializeByRefID(id: number): string { return '@' + id.toString(16); } +function serializeByContextID(id: number): string { + return '!'; +} + function escapeStringValue(value: string): string { if (value[0] === '$' || value[0] === '@') { // We need to escape $ or @ prefixed strings since we use those to encode @@ -221,7 +267,6 @@ function isObjectPrototype(object): boolean { if (!object) { return false; } - // $FlowFixMe const ObjectPrototype = Object.prototype; if (object === ObjectPrototype) { return true; @@ -311,7 +356,6 @@ function describeObjectForErrorMessage( ): string { if (isArray(objectOrArray)) { let str = '['; - // $FlowFixMe: Should be refined by now. const array: $ReadOnlyArray = objectOrArray; for (let i = 0; i < array.length; i++) { if (i > 0) { @@ -336,7 +380,6 @@ function describeObjectForErrorMessage( return str; } else { let str = '{'; - // $FlowFixMe: Should be refined by now. const object: {+[key: string | number]: ReactModel} = objectOrArray; const names = Object.keys(object); for (let i = 0; i < names.length; i++) { @@ -390,6 +433,8 @@ export function resolveModelToJSON( switch (value) { case REACT_ELEMENT_TYPE: return '$'; + case REACT_PROVIDER_TYPE: + return '!'; case REACT_LAZY_TYPE: throw new Error( 'React Lazy Components are not yet supported on the server.', @@ -412,11 +457,12 @@ export function resolveModelToJSON( element.ref, element.props, ); + 2; } catch (x) { if (typeof x === 'object' && x !== null && typeof x.then === 'function') { // Something suspended, we'll need to create a new segment and resolve it later. request.pendingChunks++; - const newSegment = createSegment(request, value); + const newSegment = createSegment(request, value, getActiveContext()); const ping = newSegment.ping; x.then(ping, ping); return serializeByRefID(newSegment.id); @@ -658,6 +704,7 @@ function emitSymbolChunk(request: Request, id: number, name: string): void { } function retrySegment(request: Request, segment: Segment): void { + switchContext(segment.context); try { let value = segment.model; while ( @@ -677,8 +724,13 @@ function retrySegment(request: Request, segment: Segment): void { element.ref, element.props, ); + 1; } - const processedChunk = processModelChunk(request, segment.id, value); + const processedChunk = processModelChunk( + request, + segment.id, + convertModelToJSON(request, {'': value}, '', value), + ); request.completedJSONChunks.push(processedChunk); } catch (x) { if (typeof x === 'object' && x !== null && typeof x.then === 'function') { @@ -696,9 +748,9 @@ function retrySegment(request: Request, segment: Segment): void { function performWork(request: Request): void { const prevDispatcher = ReactCurrentDispatcher.current; - const prevCache = currentCache; + const prevCache = getCurrentCache(); ReactCurrentDispatcher.current = Dispatcher; - currentCache = request.cache; + setCurrentCache(request.cache); try { const pingedSegments = request.pingedSegments; @@ -715,7 +767,7 @@ function performWork(request: Request): void { fatalError(request, error); } finally { ReactCurrentDispatcher.current = prevDispatcher; - currentCache = prevCache; + setCurrentCache(prevCache); } } @@ -806,56 +858,76 @@ export function startFlowing(request: Request, destination: Destination): void { } } -function unsupportedHook(): void { - throw new Error('This Hook is not supported in Server Components.'); -} - -function unsupportedRefresh(): void { - if (!currentCache) { - throw new Error( - 'Refreshing the cache is not supported in Server Components.', - ); +function importServerContexts( + contexts: + | Array<{name: string, value: ServerContextJSONValue}> + | typeof undefined, +) { + const registry: {[name: string]: ReactServerContext} = {}; + if (contexts) { + for (let i = 0; i < contexts.length; i++) { + const {name, value} = contexts[i]; + const context = createServerContext(name, null); + pushProvider(context, value); + registry[name] = context; + } } + setCurrentServerContexts(registry); + return getActiveContext(); } -let currentCache: Map | null = null; - -const Dispatcher: DispatcherType = { - useMemo(nextCreate: () => T): T { - return nextCreate(); - }, - useCallback(callback: T): T { - return callback; - }, - useDebugValue(): void {}, - useDeferredValue: (unsupportedHook: any), - useTransition: (unsupportedHook: any), - getCacheForType(resourceType: () => T): T { - if (!currentCache) { - throw new Error('Reading the cache is only supported while rendering.'); +function convertModelToJSON( + request: Request, + parent: {+[key: string]: ReactModel} | $ReadOnlyArray, + key: string, + model: ReactModel, +): JSONValue { + const isReactElement = + typeof model === 'object' && + model !== null && + (model: any).$$typeof === REACT_ELEMENT_TYPE; + + let context; + if (isReactElement) { + const element: React$Element = (model: any); + if (element.type.$$typeof === REACT_PROVIDER_TYPE) { + context = element.type._context; + pushProvider(context, element.props.value); } + } - let entry: T | void = (currentCache.get(resourceType): any); - if (entry === undefined) { - entry = resourceType(); - // TODO: Warn if undefined? - currentCache.set(resourceType, entry); + const json = resolveModelToJSON(request, parent, key, model); + + if (typeof json === 'object' && json !== null) { + if (isArray(json)) { + const jsonArray: Array = []; + for (let i = 0; i < json.length; i++) { + jsonArray[i] = convertModelToJSON(request, json, '' + i, json[i]); + } + if (context) { + popProvider(context); + } + return jsonArray; + } else { + const jsonObj: {[key: string]: JSONValue} = {}; + for (const nextKey in json) { + if (hasOwnProperty.call(json, nextKey)) { + jsonObj[nextKey] = convertModelToJSON( + request, + json, + nextKey, + json[nextKey], + ); + } + } + if (context) { + popProvider(context); + } + return jsonObj; } - return entry; - }, - readContext: (unsupportedHook: any), - useContext: (unsupportedHook: any), - useReducer: (unsupportedHook: any), - useRef: (unsupportedHook: any), - useState: (unsupportedHook: any), - useInsertionEffect: (unsupportedHook: any), - useLayoutEffect: (unsupportedHook: any), - useImperativeHandle: (unsupportedHook: any), - useEffect: (unsupportedHook: any), - useId: (unsupportedHook: any), - useMutableSource: (unsupportedHook: any), - useSyncExternalStore: (unsupportedHook: any), - useCacheRefresh(): (?() => T, ?T) => void { - return unsupportedRefresh; - }, -}; + } + if (context) { + popProvider(context); + } + return json; +} diff --git a/packages/react-server/src/ReactFlightServerConfigStream.js b/packages/react-server/src/ReactFlightServerConfigStream.js index 74a90f7a02a..c815f4ecf7c 100644 --- a/packages/react-server/src/ReactFlightServerConfigStream.js +++ b/packages/react-server/src/ReactFlightServerConfigStream.js @@ -92,10 +92,9 @@ export function processErrorChunk( export function processModelChunk( request: Request, id: number, - model: ReactModel, + jsonValue: JSONValue, ): Chunk { - const json = stringify(model, request.toJSON); - const row = serializeRowHeader('J', id) + json + '\n'; + const row = serializeRowHeader('J', id) + JSON.stringify(jsonValue) + '\n'; return stringToChunk(row); } diff --git a/packages/react/index.classic.fb.js b/packages/react/index.classic.fb.js index 33c770f00fe..122f679fcfd 100644 --- a/packages/react/index.classic.fb.js +++ b/packages/react/index.classic.fb.js @@ -26,6 +26,7 @@ export { createMutableSource, createMutableSource as unstable_createMutableSource, createRef, + createServerContext, forwardRef, isValidElement, lazy, @@ -53,10 +54,11 @@ export { useMemo, useMutableSource, useMutableSource as unstable_useMutableSource, - useSyncExternalStore, useReducer, useRef, + useServerContext, useState, + useSyncExternalStore, useTransition, useTransition as unstable_useTransition, // TODO: Remove once call sights updated to useTransition version, diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js index d4dc33a4db0..1ee0c52731f 100644 --- a/packages/react/index.experimental.js +++ b/packages/react/index.experimental.js @@ -24,6 +24,7 @@ export { createFactory, createMutableSource as unstable_createMutableSource, createRef, + createServerContext, forwardRef, isValidElement, lazy, @@ -46,10 +47,11 @@ export { useInsertionEffect, useLayoutEffect, useMemo, - useSyncExternalStore, useReducer, useRef, + useServerContext, useState, + useSyncExternalStore, useTransition, version, } from './src/React'; diff --git a/packages/react/index.js b/packages/react/index.js index e4946bf095b..aa25252e253 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -48,6 +48,7 @@ export { createFactory, createMutableSource, createRef, + createServerContext, forwardRef, isValidElement, lazy, @@ -73,6 +74,7 @@ export { useLayoutEffect, useMemo, useMutableSource, + useServerContext, useSyncExternalStore, useReducer, useRef, diff --git a/packages/react/index.modern.fb.js b/packages/react/index.modern.fb.js index 5b0d75e2146..231f094922f 100644 --- a/packages/react/index.modern.fb.js +++ b/packages/react/index.modern.fb.js @@ -25,6 +25,7 @@ export { createMutableSource, createMutableSource as unstable_createMutableSource, createRef, + createServerContext, forwardRef, isValidElement, lazy, @@ -52,10 +53,11 @@ export { useMemo, useMutableSource, useMutableSource as unstable_useMutableSource, - useSyncExternalStore, useReducer, useRef, + useServerContext, useState, + useSyncExternalStore, useTransition, useTransition as unstable_useTransition, // TODO: Remove once call sights updated to useTransition version, diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js index 53497831e00..d603d2b62c4 100644 --- a/packages/react/index.stable.js +++ b/packages/react/index.stable.js @@ -22,6 +22,7 @@ export { createElement, createFactory, createRef, + createServerContext, forwardRef, isValidElement, lazy, @@ -37,10 +38,11 @@ export { useInsertionEffect, useLayoutEffect, useMemo, - useSyncExternalStore, useReducer, useRef, + useServerContext, useState, + useSyncExternalStore, useTransition, version, } from './src/React'; diff --git a/packages/react/src/React.js b/packages/react/src/React.js index b899f51c80b..a64384347a3 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -32,6 +32,7 @@ import { isValidElement, } from './ReactElement'; import {createContext} from './ReactContext'; +import {createServerContext} from './ReactServerContext'; import {lazy} from './ReactLazy'; import {forwardRef} from './ReactForwardRef'; import {memo} from './ReactMemo'; @@ -50,6 +51,7 @@ import { useSyncExternalStore, useReducer, useRef, + useServerContext, useState, useTransition, useDeferredValue, @@ -86,6 +88,7 @@ export { Component, PureComponent, createContext, + createServerContext, forwardRef, lazy, memo, @@ -98,6 +101,7 @@ export { useLayoutEffect, useMemo, useMutableSource, + useServerContext, useSyncExternalStore, useReducer, useRef, diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 9dc7a98589e..17bc8108d02 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -14,6 +14,8 @@ import type { MutableSourceSubscribeFn, ReactContext, StartTransitionOptions, + ReactServerContext, + ServerContextJSONValue, } from 'shared/ReactTypes'; import ReactCurrentDispatcher from './ReactCurrentDispatcher'; @@ -186,6 +188,14 @@ export function useMutableSource( return dispatcher.useMutableSource(source, getSnapshot, subscribe); } +export function useServerContext( + Context: ReactServerContext, +): T { + // TODO: Warn if regular context is passed in + const dispatcher = resolveDispatcher(); + return dispatcher.useServerContext(Context); +} + export function useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js new file mode 100644 index 00000000000..8d4d93ea457 --- /dev/null +++ b/packages/react/src/ReactServerContext.js @@ -0,0 +1,63 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; + +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; + +const globalRegistry: { + [globalName: string]: ReactServerContext, +} = {}; + +export function createServerContext( + globalName: string, + defaultValue: T, +): ReactServerContext { + if (globalRegistry[globalName]) { + throw new Error('ServerContext in that name already exists'); + } + const context: ReactServerContext = { + $$typeof: REACT_CONTEXT_TYPE, + // As a workaround to support multiple concurrent renderers, we categorize + // some renderers as primary and others as secondary. We only expect + // there to be two concurrent renderers at most: React Native (primary) and + // Fabric (secondary); React DOM (primary) and React ART (secondary). + // Secondary renderers store their context values on separate fields. + _currentValue: defaultValue, + _currentValue2: defaultValue, + // Used to track how many concurrent renderers this context currently + // supports within in a single renderer. Such as parallel server rendering. + _threadCount: 0, + // These are circular + Provider: (null: any), + displayName: globalName, + }; + + context.Provider = { + $$typeof: REACT_PROVIDER_TYPE, + _context: context, + }; + + if (__DEV__) { + context._currentRenderer = null; + context._currentRenderer2 = null; + } + globalRegistry[globalName] = context; + return context; +} + +export function getOrCreateContextByName(name: string) { + if (!globalRegistry[name]) { + globalRegistry[name] = createServerContext(name, null); + } + return globalRegistry[name]; +} diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 066d20552d6..2282c05c4b2 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -38,13 +38,19 @@ export type ReactProvider = { export type ReactProviderType = { $$typeof: Symbol | number, - _context: ReactContext, + _context: ReactContext | ReactServerContext, + ... +}; + +export type ReactServerProviderType = { + $$typeof: Symbol | number, + _context: ReactServerContext, ... }; export type ReactConsumer = { $$typeof: Symbol | number, - type: ReactContext, + type: ReactContext | ReactServerContext, key: null | string, ref: null, props: { @@ -70,6 +76,26 @@ export type ReactContext = { ... }; +export type ServerContextJSONValue = + | string + | boolean + | number + | null + | $ReadOnlyArray + | {+[key: string]: ServerContextJSONValue}; + +export type ReactServerContext = { + $$typeof: Symbol | number, + Provider: ReactServerProviderType, + _currentValue: T, + _currentValue2: T, + _currentRenderer?: Object | null, + _currentRenderer2?: Object | null, + _threadCount: number, + +displayName: string, + ... +}; + export type ReactPortal = { $$typeof: Symbol | number, key: null | string, diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 36432e56aca..907f34d4cae 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -41,7 +41,7 @@ function getWrappedName( } // Keep in sync with react-reconciler/getComponentNameFromFiber -function getContextName(type: ReactContext) { +function getContextName(type: ReactContext | ReactServerContext) { return type.displayName || 'Context'; } diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 367b736931c..66feddb5c01 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -410,5 +410,7 @@ "422": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", "423": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", "424": "Text content does not match server-rendered HTML.", - "425": "A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition." + "425": "A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition.", + "426": "An error occurred during hydration. The server HTML was replaced with client content", + "427": "useServerContext is only supported while rendering." } From 640c4a8cadf7f6a9ba07f2ac1d11e0a29e226adf Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Feb 2022 16:21:14 -0500 Subject: [PATCH 30/83] 1 more test --- .../src/__tests__/ReactFlight-test.js | 20 +++++++++++++++++++ .../src/ReactFiberNewContext.new.js | 1 - .../react-server/src/ReactFlightServer.js | 4 ++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 2db829fd7cd..54ab8aa87bf 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -500,4 +500,24 @@ describe('ReactFlight', () => { expect(Scheduler).toHaveYielded(['ClientBar']); expect(ReactNoop).toMatchRenderedOutput(hi this is server); }); + + it('takes ServerContext from client for refetching usecases', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + function Bar() { + return {React.useServerContext(ServerContext)}; + } + const transport = ReactNoopFlightServer.render(, { + context: [{name: 'ServerContext', value: 'Override'}], + }); + + act(() => { + const flightModel = ReactNoopFlightClient.read(transport); + ReactNoop.render(flightModel); + }); + + expect(ReactNoop).toMatchRenderedOutput(Override); + }); }); diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index e73158d2f77..a871401d9a1 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -92,7 +92,6 @@ export function pushProvider( context: ReactContext | ReactServerContext, nextValue: T, ): void { - debugger; if (isPrimaryRenderer) { push(valueCursor, context._currentValue, providerFiber); diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 3324be545d1..cc4893282be 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -38,7 +38,7 @@ import { isModuleReference, } from './ReactFlightServerConfig'; -import {createServerContext} from 'react/src/ReactServerContext'; +import {getOrCreateContextByName} from 'react/src/ReactServerContext'; import { Dispatcher, getCurrentCache, @@ -867,7 +867,7 @@ function importServerContexts( if (contexts) { for (let i = 0; i < contexts.length; i++) { const {name, value} = contexts[i]; - const context = createServerContext(name, null); + const context = getOrCreateContextByName(name); pushProvider(context, value); registry[name] = context; } From c83c3cc6c46502b3a88bdbac35cc91cd0b4c1ba0 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Feb 2022 16:31:43 -0500 Subject: [PATCH 31/83] rm unused function --- packages/react-server/src/ReactFlightServer.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index cc4893282be..e8e64e40f8a 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -249,10 +249,6 @@ function serializeByRefID(id: number): string { return '@' + id.toString(16); } -function serializeByContextID(id: number): string { - return '!'; -} - function escapeStringValue(value: string): string { if (value[0] === '$' || value[0] === '@') { // We need to escape $ or @ prefixed strings since we use those to encode From 64621f01e3d5a70d6ce201c06fe416513fec9a36 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 14 Feb 2022 10:45:31 -0500 Subject: [PATCH 32/83] flow+prettier --- .../react-client/src/ReactFlightClient.js | 4 +- .../src/__tests__/ReactFlight-test.js | 359 +++++++++--------- .../react-debug-tools/src/ReactDebugHooks.js | 15 +- .../src/backend/types.js | 6 +- .../src/server/ReactPartialRenderer.js | 19 +- .../src/server/ReactPartialRendererHooks.js | 4 +- packages/react-is/src/ReactIs.js | 2 + .../src/ReactFiberHooks.new.js | 155 +++++--- .../src/ReactFiberHooks.old.js | 155 +++++--- .../src/ReactFiberNewContext.new.js | 8 +- .../src/ReactFiberNewContext.old.js | 8 +- .../src/ReactFiberWorkLoop.old.js | 1 - .../src/ReactInternalTypes.js | 8 +- .../src/getComponentNameFromFiber.js | 8 +- .../ReactFlightDOMRelayServerHostConfig.js | 37 +- .../ReactFlightNativeRelayServerHostConfig.js | 6 +- packages/react-server/src/ReactFlightHooks.js | 9 +- .../react-server/src/ReactFlightServer.js | 115 ++---- .../src/ReactFlightServerConfigStream.js | 5 +- .../src/ReactSuspenseTestUtils.js | 1 + packages/react/src/ReactHooks.js | 1 + packages/react/src/ReactServerContext.js | 49 ++- packages/shared/ReactFeatureFlags.js | 1 + packages/shared/ReactSymbols.js | 1 + packages/shared/ReactTypes.js | 3 +- .../forks/ReactFeatureFlags.native-fb.js | 2 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.testing.js | 1 + .../forks/ReactFeatureFlags.testing.www.js | 2 +- .../shared/forks/ReactFeatureFlags.www.js | 1 + packages/shared/getComponentNameFromType.js | 6 +- scripts/error-codes/codes.json | 3 +- 34 files changed, 581 insertions(+), 417 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index acc3436f7c4..e43446435d5 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -24,7 +24,7 @@ import { parseModel, } from './ReactFlightClientHostConfig'; -import {getOrCreateContextByName} from 'react/src/ReactServerContext'; +import {getOrCreateServerContext} from 'react/src/ReactServerContext'; import { REACT_LAZY_TYPE, @@ -346,7 +346,7 @@ export function parseModelTuple( return createElement(tuple[1], tuple[2], tuple[3]); case REACT_PROVIDER_TYPE: return createElement( - getOrCreateContextByName((tuple[1]: any)).Provider, + getOrCreateServerContext((tuple[1]: any)).Provider, tuple[2], tuple[3], ); diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 54ab8aa87bf..d86e9cad522 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -305,219 +305,232 @@ describe('ReactFlight', () => { ); }); - it('supports basic createServerContext usage', () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'hello from server', - ); - function Foo() { - const context = React.useServerContext(ServerContext); - return
{context}
; - } + describe('ServerContext', () => { + // @gate enableServerContext + it('supports basic createServerContext usage', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'hello from server', + ); + function Foo() { + const context = React.useServerContext(ServerContext); + return
{context}
; + } - const transport = ReactNoopFlightServer.render(); - act(() => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; - ReactNoop.render(ReactNoopFlightClient.read(transport)); + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(
hello from server
); }); - expect(ReactNoop).toMatchRenderedOutput(
hello from server
); - }); + // @gate enableServerContext + it('propagates ServerContext providers in flight', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); - it('propagates ServerContext providers in flight', () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); + function Foo() { + return ( +
+ + + +
+ ); + } + function Bar() { + const context = React.useServerContext(ServerContext); + return context; + } - function Foo() { - return ( -
- - - -
- ); - } - function Bar() { - const context = React.useServerContext(ServerContext); - return context; - } + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); - const transport = ReactNoopFlightServer.render(); - act(() => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; - ReactNoop.render(ReactNoopFlightClient.read(transport)); + expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); }); - expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); - }); - - it('propagates ServerContext and cleansup providers in flight', () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); + // @gate enableServerContext + it('propagates ServerContext and cleansup providers in flight', () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); - function Foo() { - return ( - <> - - + function Foo() { + return ( + <> + + + + + + + - + - - - - - - - ); - } - function Bar() { - const context = React.useServerContext(ServerContext); - return {context}; - } - - const transport = ReactNoopFlightServer.render(); - act(() => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; - ReactNoop.render(ReactNoopFlightClient.read(transport)); - }); + + ); + } + function Bar() { + const context = React.useServerContext(ServerContext); + return {context}; + } - expect(ReactNoop).toMatchRenderedOutput( - <> - hi this is server - hi this is server2 - hi this is server outer - hi this is server outer2 - default hello from server - , - ); - }); + const transport = ReactNoopFlightServer.render(); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); - it('propagates ServerContext providers in flight after suspending', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); + expect(ReactNoop).toMatchRenderedOutput( + <> + hi this is server + hi this is server2 + hi this is server outer + hi this is server outer2 + default hello from server + , + ); + }); - function Foo() { - return ( -
- - - - - -
+ // @gate enableServerContext + it('propagates ServerContext providers in flight after suspending', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', ); - } - let resolve; - const promise = new Promise(res => { - resolve = () => { - promise.unsuspend = true; - res(); - }; - }); + function Foo() { + return ( +
+ + + + + +
+ ); + } + + let resolve; + const promise = new Promise(res => { + resolve = () => { + promise.unsuspend = true; + res(); + }; + }); - function Bar() { - if (!promise.unsuspend) { - Scheduler.unstable_yieldValue('suspended'); - throw promise; + function Bar() { + if (!promise.unsuspend) { + Scheduler.unstable_yieldValue('suspended'); + throw promise; + } + Scheduler.unstable_yieldValue('rendered'); + const context = React.useServerContext(ServerContext); + return context; } - Scheduler.unstable_yieldValue('rendered'); - const context = React.useServerContext(ServerContext); - return context; - } - const transport = ReactNoopFlightServer.render(); + const transport = ReactNoopFlightServer.render(); - expect(Scheduler).toHaveYielded(['suspended']); + expect(Scheduler).toHaveYielded(['suspended']); - await act(async () => { - resolve(); - await promise; - jest.runAllImmediates(); - }); + await act(async () => { + resolve(); + await promise; + jest.runAllImmediates(); + }); - expect(Scheduler).toHaveYielded(['rendered']); + expect(Scheduler).toHaveYielded(['rendered']); - act(() => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; - ReactNoop.render(ReactNoopFlightClient.read(transport)); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + ReactNoop.render(ReactNoopFlightClient.read(transport)); + }); + + expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); }); - expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); - }); + // @gate enableServerContext + it('serializes ServerContext to client', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); - it('serializes ServerContext to client', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); + function ClientBar() { + Scheduler.unstable_yieldValue('ClientBar'); + const context = React.useServerContext(ServerContext); + return {context}; + } - function ClientBar() { - Scheduler.unstable_yieldValue('ClientBar'); - const context = React.useServerContext(ServerContext); - return {context}; - } + const Bar = moduleReference(ClientBar); - const Bar = moduleReference(ClientBar); + function Foo() { + return ( + + + + ); + } - function Foo() { - return ( - - - - ); - } + const model = { + foo: , + }; - const model = { - foo: , - }; + const transport = ReactNoopFlightServer.render(model); - const transport = ReactNoopFlightServer.render(model); + expect(Scheduler).toHaveYielded([]); - expect(Scheduler).toHaveYielded([]); + act(() => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + const flightModel = ReactNoopFlightClient.read(transport); + ReactNoop.render(flightModel.foo); + }); - act(() => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; - const flightModel = ReactNoopFlightClient.read(transport); - ReactNoop.render(flightModel.foo); + expect(Scheduler).toHaveYielded(['ClientBar']); + expect(ReactNoop).toMatchRenderedOutput(hi this is server); }); - expect(Scheduler).toHaveYielded(['ClientBar']); - expect(ReactNoop).toMatchRenderedOutput(hi this is server); - }); + // @gate enableServerContext + it('takes ServerContext from client for refetching usecases', async () => { + const ServerContext = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + function Bar() { + return {React.useServerContext(ServerContext)}; + } + const transport = ReactNoopFlightServer.render(, { + context: [ + { + name: 'ServerContext', + value: 'Override', + }, + ], + }); - it('takes ServerContext from client for refetching usecases', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); - function Bar() { - return {React.useServerContext(ServerContext)}; - } - const transport = ReactNoopFlightServer.render(, { - context: [{name: 'ServerContext', value: 'Override'}], - }); + act(() => { + const flightModel = ReactNoopFlightClient.read(transport); + ReactNoop.render(flightModel); + }); - act(() => { - const flightModel = ReactNoopFlightClient.read(transport); - ReactNoop.render(flightModel); + expect(ReactNoop).toMatchRenderedOutput(Override); }); - - expect(ReactNoop).toMatchRenderedOutput(Override); }); }); diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 5ebca44b925..415d1d99e20 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -351,7 +351,8 @@ function useId(): string { const Dispatcher: DispatcherType = { getCacheForType, - readContext, + // TODO: figure out why flow is complaining here + readContext: (readContext: any), useCacheRefresh, useCallback, useContext, @@ -694,12 +695,16 @@ export function inspectHooks( return buildTree(rootStack, readHookLog, includeHooksSource); } -function setupContexts(contextMap: Map, any>, fiber: Fiber) { +function setupContexts( + contextMap: Map | ReactServerContext, any>, + fiber: Fiber, +) { let current = fiber; while (current) { if (current.tag === ContextProvider) { const providerType: ReactProviderType = current.type; - const context: ReactContext = providerType._context; + const context: ReactContext | ReactServerContext = + providerType._context; if (!contextMap.has(context)) { // Store the current value that we're going to restore later. contextMap.set(context, context._currentValue); @@ -711,7 +716,9 @@ function setupContexts(contextMap: Map, any>, fiber: Fiber) { } } -function restoreContexts(contextMap: Map, any>) { +function restoreContexts( + contextMap: Map | ReactServerContext, any>, +) { contextMap.forEach((value, context) => (context._currentValue = value)); } diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index c1183a36c06..19327019e59 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactContext, Wakeable} from 'shared/ReactTypes'; +import type { + ReactContext, + ReactServerContext, + Wakeable, +} from 'shared/ReactTypes'; import type {Source} from 'shared/ReactElementType'; import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; import type { diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 41c28856804..9215a9b49ae 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -10,7 +10,11 @@ import type {ThreadID} from './ReactThreadIDAllocator'; import type {ReactElement} from 'shared/ReactElementType'; import type {LazyComponent} from 'react/src/ReactLazy'; -import type {ReactProvider, ReactContext} from 'shared/ReactTypes'; +import type { + ReactProvider, + ReactContext, + ReactServerContext, +} from 'shared/ReactTypes'; import * as React from 'react'; import isArray from 'shared/isArray'; @@ -777,7 +781,7 @@ class ReactDOMServerRenderer { suspenseDepth: number; contextIndex: number; - contextStack: Array>; + contextStack: Array | ReactServerContext>; contextValueStack: Array; contextProviderStack: ?Array>; // DEV-only @@ -833,9 +837,10 @@ class ReactDOMServerRenderer { * https://github.com/facebook/react/pull/12985#issuecomment-396301248 */ - pushProvider(provider: ReactProvider): void { + pushProvider(provider: ReactProvider): void { const index = ++this.contextIndex; - const context: ReactContext = provider.type._context; + const context: ReactContext | ReactServerContext = + provider.type._context; const threadID = this.threadID; validateContextBounds(context, threadID); const previousValue = context[threadID]; @@ -860,7 +865,8 @@ class ReactDOMServerRenderer { } } - const context: ReactContext = this.contextStack[index]; + const context: ReactContext | ReactServerContext = this + .contextStack[index]; const previousValue = this.contextValueStack[index]; // "Hide" these null assignments from Flow by using `any` @@ -882,7 +888,8 @@ class ReactDOMServerRenderer { clearProviders(): void { // Restore any remaining providers on the stack to previous values for (let index = this.contextIndex; index >= 0; index--) { - const context: ReactContext = this.contextStack[index]; + const context: ReactContext | ReactServerContext = this + .contextStack[index]; const previousValue = this.contextValueStack[index]; context[this.threadID] = previousValue; } diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 1da0127a131..b36cd289caa 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -15,7 +15,7 @@ import type { MutableSourceSubscribeFn, ReactContext, ReactServerContext, - ServerContextValue, + ServerContextJSONValue, } from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; @@ -252,7 +252,7 @@ function useContext(context: ReactContext): T { return context[threadID]; } -function useServerContext( +function useServerContext( context: ReactServerContext, ): T { if (__DEV__) { diff --git a/packages/react-is/src/ReactIs.js b/packages/react-is/src/ReactIs.js index dd81ec03615..8c5d26a08c2 100644 --- a/packages/react-is/src/ReactIs.js +++ b/packages/react-is/src/ReactIs.js @@ -11,6 +11,7 @@ import { REACT_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_TYPE, REACT_ELEMENT_TYPE, REACT_FORWARD_REF_TYPE, REACT_FRAGMENT_TYPE, @@ -43,6 +44,7 @@ export function typeOf(object: any) { const $$typeofType = type && type.$$typeof; switch ($$typeofType) { + case REACT_SERVER_CONTEXT_TYPE: case REACT_CONTEXT_TYPE: case REACT_FORWARD_REF_TYPE: case REACT_LAZY_TYPE: diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 697423c36d9..f5786784797 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -35,6 +35,7 @@ import { enableSuspenseLayoutEffectSemantics, enableUseMutableSource, enableTransitionTracing, + enableServerContext, } from 'shared/ReactFeatureFlags'; import { @@ -2398,12 +2399,33 @@ function getCacheForType(resourceType: () => T): T { return cacheForType; } +function mountServerContext( + context: ReactServerContext, +): T { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + mountHookTypesDev(); + return readContext(context); +} + +function updateServerContext( + context: ReactServerContext, +): T { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); +} + export const ContextOnlyDispatcher: Dispatcher = { readContext, useCallback: throwInvalidHookError, useContext: throwInvalidHookError, - useServerContext: throwInvalidHookError, useEffect: throwInvalidHookError, useImperativeHandle: throwInvalidHookError, useInsertionEffect: throwInvalidHookError, @@ -2427,12 +2449,15 @@ if (enableCache) { (ContextOnlyDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError; } +if (enableServerContext) { + (ContextOnlyDispatcher: Dispatcher).useServerContext = throwInvalidHookError; +} + const HooksDispatcherOnMount: Dispatcher = { readContext, useCallback: mountCallback, useContext: readContext, - useServerContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, useLayoutEffect: mountLayoutEffect, @@ -2455,13 +2480,15 @@ if (enableCache) { (HooksDispatcherOnMount: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnMount: Dispatcher).useCacheRefresh = mountRefresh; } +if (enableServerContext) { + (HooksDispatcherOnMount: Dispatcher).useServerContext = readContext; +} const HooksDispatcherOnUpdate: Dispatcher = { readContext, useCallback: updateCallback, useContext: readContext, - useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2484,13 +2511,15 @@ if (enableCache) { (HooksDispatcherOnUpdate: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnUpdate: Dispatcher).useCacheRefresh = updateRefresh; } +if (enableServerContext) { + (HooksDispatcherOnUpdate: Dispatcher).useServerContext = readContext; +} const HooksDispatcherOnRerender: Dispatcher = { readContext, useCallback: updateCallback, useContext: readContext, - useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2513,6 +2542,9 @@ if (enableCache) { (HooksDispatcherOnRerender: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnRerender: Dispatcher).useCacheRefresh = updateRefresh; } +if (enableServerContext) { + (HooksDispatcherOnRerender: Dispatcher).useServerContext = readContext; +} let HooksDispatcherOnMountInDEV: Dispatcher | null = null; let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null; @@ -2556,13 +2588,6 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - mountHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2695,6 +2720,9 @@ if (__DEV__) { return mountRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnMountInDEV: Dispatcher).useServerContext = mountServerContext; + } HooksDispatcherOnMountWithHookTypesInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2710,13 +2738,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2844,6 +2865,9 @@ if (__DEV__) { return mountRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useServerContext = updateServerContext; + } HooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2859,13 +2883,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2993,6 +3010,12 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = updateServerContext; + } + + if (!enableServerContext) { + } HooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3009,13 +3032,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3143,6 +3159,9 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + HooksDispatcherOnRerenderInDEV.useServerContext = updateServerContext; + } InvalidNestedHooksDispatcherOnMountInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3161,14 +3180,6 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3311,6 +3322,22 @@ if (__DEV__) { }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return readContext(context); + }; + } + InvalidNestedHooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); @@ -3328,14 +3355,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3477,9 +3496,24 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }; + } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3496,14 +3530,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3645,4 +3671,19 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }; + } } diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 0869742c8c5..27cb6e4551c 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -35,6 +35,7 @@ import { enableSuspenseLayoutEffectSemantics, enableUseMutableSource, enableTransitionTracing, + enableServerContext, } from 'shared/ReactFeatureFlags'; import { @@ -2398,12 +2399,33 @@ function getCacheForType(resourceType: () => T): T { return cacheForType; } +function mountServerContext( + context: ReactServerContext, +): T { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + mountHookTypesDev(); + return readContext(context); +} + +function updateServerContext( + context: ReactServerContext, +): T { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + updateHookTypesDev(); + return readContext(context); +} + export const ContextOnlyDispatcher: Dispatcher = { readContext, useCallback: throwInvalidHookError, useContext: throwInvalidHookError, - useServerContext: throwInvalidHookError, useEffect: throwInvalidHookError, useImperativeHandle: throwInvalidHookError, useInsertionEffect: throwInvalidHookError, @@ -2427,12 +2449,15 @@ if (enableCache) { (ContextOnlyDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError; } +if (enableServerContext) { + (ContextOnlyDispatcher: Dispatcher).useServerContext = throwInvalidHookError; +} + const HooksDispatcherOnMount: Dispatcher = { readContext, useCallback: mountCallback, useContext: readContext, - useServerContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, useLayoutEffect: mountLayoutEffect, @@ -2455,13 +2480,15 @@ if (enableCache) { (HooksDispatcherOnMount: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnMount: Dispatcher).useCacheRefresh = mountRefresh; } +if (enableServerContext) { + (HooksDispatcherOnMount: Dispatcher).useServerContext = readContext; +} const HooksDispatcherOnUpdate: Dispatcher = { readContext, useCallback: updateCallback, useContext: readContext, - useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2484,13 +2511,15 @@ if (enableCache) { (HooksDispatcherOnUpdate: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnUpdate: Dispatcher).useCacheRefresh = updateRefresh; } +if (enableServerContext) { + (HooksDispatcherOnUpdate: Dispatcher).useServerContext = readContext; +} const HooksDispatcherOnRerender: Dispatcher = { readContext, useCallback: updateCallback, useContext: readContext, - useServerContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useInsertionEffect: updateInsertionEffect, @@ -2513,6 +2542,9 @@ if (enableCache) { (HooksDispatcherOnRerender: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnRerender: Dispatcher).useCacheRefresh = updateRefresh; } +if (enableServerContext) { + (HooksDispatcherOnRerender: Dispatcher).useServerContext = readContext; +} let HooksDispatcherOnMountInDEV: Dispatcher | null = null; let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null; @@ -2556,13 +2588,6 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - mountHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2695,6 +2720,9 @@ if (__DEV__) { return mountRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnMountInDEV: Dispatcher).useServerContext = mountServerContext; + } HooksDispatcherOnMountWithHookTypesInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2710,13 +2738,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2844,6 +2865,9 @@ if (__DEV__) { return mountRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useServerContext = updateServerContext; + } HooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2859,13 +2883,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -2993,6 +3010,12 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (HooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = updateServerContext; + } + + if (!enableServerContext) { + } HooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3009,13 +3032,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3143,6 +3159,9 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + HooksDispatcherOnRerenderInDEV.useServerContext = updateServerContext; + } InvalidNestedHooksDispatcherOnMountInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3161,14 +3180,6 @@ if (__DEV__) { mountHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3311,6 +3322,22 @@ if (__DEV__) { }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return readContext(context); + }; + } + InvalidNestedHooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); @@ -3328,14 +3355,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3477,9 +3496,24 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }; + } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext | ReactServerContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3496,14 +3530,6 @@ if (__DEV__) { updateHookTypesDev(); return readContext(context); }, - useServerContext( - context: ReactServerContext, - ): T { - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }, useEffect( create: () => (() => void) | void, deps: Array | void | null, @@ -3645,4 +3671,19 @@ if (__DEV__) { return updateRefresh(); }; } + if (enableServerContext) { + (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = < + T: ServerContextJSONValue, + >( + context: ReactServerContext, + ): T => { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + currentHookNameInDev = 'useServerContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }; + } } diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index a871401d9a1..480e86dbfd6 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -129,16 +129,16 @@ export function pushProvider( } } -export function popProvider( - context: ReactContext | ReactServerContext, +export function popProvider( + context: ReactContext | ReactServerContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = (currentValue: any); + context._currentValue = currentValue; } else { - context._currentValue2 = (currentValue: any); + context._currentValue2 = currentValue; } } diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 0adc1d03c6b..78cca5e23b4 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -129,16 +129,16 @@ export function pushProvider( } } -export function popProvider( - context: ReactContext | ReactServerContext, +export function popProvider( + context: ReactContext | ReactServerContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = (currentValue: any); + context._currentValue = currentValue; } else { - context._currentValue2 = (currentValue: any); + context._currentValue2 = currentValue; } } diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index d8bb6b16e29..9d8528b75fe 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -1555,7 +1555,6 @@ function handleError(root, thrownValue): void { ); } } - throwException( root, erroredWork.return, diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 52a96dfc96d..8a0b2521542 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -53,14 +53,14 @@ export type HookType = export type ContextDependency = { context: ReactContext | ReactServerContext, - next: ContextDependency | null, + next: ContextDependency | null, memoizedValue: T, ... }; export type Dependencies = { lanes: Lanes, - firstContext: ContextDependency | null, + firstContext: ContextDependency | null, ... }; @@ -384,9 +384,9 @@ export type Dispatcher = {| getSnapshot: MutableSourceGetSnapshotFn, subscribe: MutableSourceSubscribeFn, ): Snapshot, - useServerContext( + useServerContext?: ( context: ReactServerContext, - ): T, + ) => T, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react-reconciler/src/getComponentNameFromFiber.js b/packages/react-reconciler/src/getComponentNameFromFiber.js index 5cb87189c97..a05595624b4 100644 --- a/packages/react-reconciler/src/getComponentNameFromFiber.js +++ b/packages/react-reconciler/src/getComponentNameFromFiber.js @@ -7,7 +7,11 @@ * @flow */ -import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; +import type { + ReactContext, + ReactServerContext, + ReactProviderType, +} from 'shared/ReactTypes'; import { FunctionComponent, @@ -53,7 +57,7 @@ function getWrappedName( } // Keep in sync with shared/getComponentNameFromType -function getContextName(type: ReactContext) { +function getContextName(type: ReactContext | ReactServerContext) { return type.displayName || 'Context'; } diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js index b891c7df00a..971eca0908c 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js @@ -75,12 +75,45 @@ export function processErrorChunk( ]; } +function convertModelToJSON( + request: Request, + parent: {+[key: string]: ReactModel} | $ReadOnlyArray, + key: string, + model: ReactModel, +): JSONValue { + const json = resolveModelToJSON(request, parent, key, model); + if (typeof json === 'object' && json !== null) { + if (isArray(json)) { + const jsonArray: Array = []; + for (let i = 0; i < json.length; i++) { + jsonArray[i] = convertModelToJSON(request, json, '' + i, json[i]); + } + return jsonArray; + } else { + const jsonObj: {[key: string]: JSONValue} = {}; + for (const nextKey in json) { + if (hasOwnProperty.call(json, nextKey)) { + jsonObj[nextKey] = convertModelToJSON( + request, + json, + nextKey, + json[nextKey], + ); + } + } + return jsonObj; + } + } + return json; +} + export function processModelChunk( request: Request, id: number, - jsonValue: JSONValue, + model: ReactModel, ): Chunk { - return ['J', id, jsonValue]; + const json = convertModelToJSON(request, {}, '', model); + return ['J', id, json]; } export function processModuleChunk( diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js index c6e735a3350..980652b6890 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js @@ -103,13 +103,13 @@ function convertModelToJSON( } return json; } - export function processModelChunk( request: Request, id: number, - jsonValue: JSONValue, + model: ReactModel, ): Chunk { - return ['J', id, jsonValue]; + const json = convertModelToJSON(request, {}, '', model); + return ['J', id, json]; } export function processModuleChunk( diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index 36b37eb4a5d..b87b4b87732 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -9,15 +9,20 @@ import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes'; import type { + ReactContext, ReactServerContext, ServerContextJSONValue, } from 'shared/ReactTypes'; +import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; import {readContext as readContextImpl} from './ReactFlightNewContext'; function readContext( - context: ReactServerContext, + context: ReactContext | ReactServerContext, ): T { if (__DEV__) { + if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { + console.error('Only ServerContext is supported in Flight'); + } if (currentCache === null) { console.error( 'Context can only be read while React is rendering. ' + @@ -27,7 +32,7 @@ function readContext( ); } } - return readContextImpl(context); + return readContextImpl(((context: any): ReactServerContext)); } export const Dispatcher: DispatcherType = { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index e8e64e40f8a..911f28a2300 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -38,7 +38,7 @@ import { isModuleReference, } from './ReactFlightServerConfig'; -import {getOrCreateContextByName} from 'react/src/ReactServerContext'; +import {getOrCreateServerContext} from 'react/src/ReactServerContext'; import { Dispatcher, getCurrentCache, @@ -50,7 +50,6 @@ import { popProvider, switchContext, getActiveContext, - rootContextSnapshot, } from './ReactFlightNewContext'; import { @@ -60,6 +59,7 @@ import { REACT_LAZY_TYPE, REACT_MEMO_TYPE, REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, } from 'shared/ReactSymbols'; import ReactSharedInternals from 'shared/ReactSharedInternals'; @@ -209,7 +209,15 @@ function attemptResolveElement( return attemptResolveElement(type.type, key, ref, props); } case REACT_PROVIDER_TYPE: { - return [REACT_PROVIDER_TYPE, type._context.displayName, key, props]; + parentsWithContextStack.push(type._context); + pushProvider(type._context, props.value); + return [ + REACT_PROVIDER_TYPE, + type._context.displayName, + key, + props, + type._context, + ]; } } } @@ -404,6 +412,23 @@ function describeObjectForErrorMessage( } } +function isReactElement(value: mixed) { + return ( + typeof value === 'object' && + value !== null && + (value: any).$$typeof === REACT_ELEMENT_TYPE + ); +} + +// Save all of the parents/contexts as we recursively stringify onto this stack +// so that we can popContexts as we recurse into neighbors. +const parentsWithContextStack: Array< + | {+[key: string | number]: ReactModel} + | $ReadOnlyArray + | ReactServerContext + | ReactModel, +> = []; + export function resolveModelToJSON( request: Request, parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray, @@ -438,11 +463,7 @@ export function resolveModelToJSON( } // Resolve server components. - while ( - typeof value === 'object' && - value !== null && - value.$$typeof === REACT_ELEMENT_TYPE - ) { + while (isReactElement(value)) { // TODO: Concatenate keys of parents onto children. const element: React$Element = (value: any); try { @@ -453,7 +474,6 @@ export function resolveModelToJSON( element.ref, element.props, ); - 2; } catch (x) { if (typeof x === 'object' && x !== null && typeof x.then === 'function') { // Something suspended, we'll need to create a new segment and resolve it later. @@ -479,6 +499,11 @@ export function resolveModelToJSON( return null; } + if (value.$$typeof === REACT_SERVER_CONTEXT_TYPE && key === '4') { + popProvider((value: any)); + return (undefined: any); + } + if (typeof value === 'object') { if (isModuleReference(value)) { const moduleReference: ModuleReference = (value: any); @@ -557,6 +582,7 @@ export function resolveModelToJSON( } } } + return value; } @@ -703,11 +729,7 @@ function retrySegment(request: Request, segment: Segment): void { switchContext(segment.context); try { let value = segment.model; - while ( - typeof value === 'object' && - value !== null && - value.$$typeof === REACT_ELEMENT_TYPE - ) { + while (isReactElement(value)) { // TODO: Concatenate keys of parents onto children. const element: React$Element = (value: any); // Attempt to render the server component. @@ -720,13 +742,8 @@ function retrySegment(request: Request, segment: Segment): void { element.ref, element.props, ); - 1; } - const processedChunk = processModelChunk( - request, - segment.id, - convertModelToJSON(request, {'': value}, '', value), - ); + const processedChunk = processModelChunk(request, segment.id, value); request.completedJSONChunks.push(processedChunk); } catch (x) { if (typeof x === 'object' && x !== null && typeof x.then === 'function') { @@ -863,7 +880,7 @@ function importServerContexts( if (contexts) { for (let i = 0; i < contexts.length; i++) { const {name, value} = contexts[i]; - const context = getOrCreateContextByName(name); + const context = getOrCreateServerContext(name); pushProvider(context, value); registry[name] = context; } @@ -871,59 +888,3 @@ function importServerContexts( setCurrentServerContexts(registry); return getActiveContext(); } - -function convertModelToJSON( - request: Request, - parent: {+[key: string]: ReactModel} | $ReadOnlyArray, - key: string, - model: ReactModel, -): JSONValue { - const isReactElement = - typeof model === 'object' && - model !== null && - (model: any).$$typeof === REACT_ELEMENT_TYPE; - - let context; - if (isReactElement) { - const element: React$Element = (model: any); - if (element.type.$$typeof === REACT_PROVIDER_TYPE) { - context = element.type._context; - pushProvider(context, element.props.value); - } - } - - const json = resolveModelToJSON(request, parent, key, model); - - if (typeof json === 'object' && json !== null) { - if (isArray(json)) { - const jsonArray: Array = []; - for (let i = 0; i < json.length; i++) { - jsonArray[i] = convertModelToJSON(request, json, '' + i, json[i]); - } - if (context) { - popProvider(context); - } - return jsonArray; - } else { - const jsonObj: {[key: string]: JSONValue} = {}; - for (const nextKey in json) { - if (hasOwnProperty.call(json, nextKey)) { - jsonObj[nextKey] = convertModelToJSON( - request, - json, - nextKey, - json[nextKey], - ); - } - } - if (context) { - popProvider(context); - } - return jsonObj; - } - } - if (context) { - popProvider(context); - } - return json; -} diff --git a/packages/react-server/src/ReactFlightServerConfigStream.js b/packages/react-server/src/ReactFlightServerConfigStream.js index c815f4ecf7c..74a90f7a02a 100644 --- a/packages/react-server/src/ReactFlightServerConfigStream.js +++ b/packages/react-server/src/ReactFlightServerConfigStream.js @@ -92,9 +92,10 @@ export function processErrorChunk( export function processModelChunk( request: Request, id: number, - jsonValue: JSONValue, + model: ReactModel, ): Chunk { - const row = serializeRowHeader('J', id) + JSON.stringify(jsonValue) + '\n'; + const json = stringify(model, request.toJSON); + const row = serializeRowHeader('J', id) + json + '\n'; return stringToChunk(row); } diff --git a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js index d75f1f7c9b4..27115bf6b4b 100644 --- a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js +++ b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js @@ -46,6 +46,7 @@ export function waitForSuspense(fn: () => T): Promise { useMutableSource: unsupported, useSyncExternalStore: unsupported, useCacheRefresh: unsupported, + useServerContext: unsupported, }; // Not using async/await because we don't compile it. return new Promise((resolve, reject) => { diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 17bc8108d02..d200348c75d 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -193,6 +193,7 @@ export function useServerContext( ): T { // TODO: Warn if regular context is passed in const dispatcher = resolveDispatcher(); + // $FlowFixMe This is unstable, thus optional return dispatcher.useServerContext(Context); } diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 8d4d93ea457..1d15a9b3161 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -7,13 +7,18 @@ * @flow */ -import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; +import { + REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, +} from 'shared/ReactSymbols'; import type { ReactServerContext, ServerContextJSONValue, } from 'shared/ReactTypes'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; + const globalRegistry: { [globalName: string]: ReactServerContext, } = {}; @@ -22,21 +27,40 @@ export function createServerContext( globalName: string, defaultValue: T, ): ReactServerContext { - if (globalRegistry[globalName]) { - throw new Error('ServerContext in that name already exists'); + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + if (!globalRegistry[globalName]) { + globalRegistry[globalName] = _createServerContext(globalName, defaultValue); } + const context = globalRegistry[globalName]; + if (!context._definitionLoaded) { + context._currentValue = defaultValue; + context._currentValue2 = defaultValue; + context._definitionLoaded = true; + } else { + throw new Error(`ServerContext: ${globalName} already defined`); + } + return context; +} + +function _createServerContext( + globalName: string, + defaultValue?: T, +): ReactServerContext { const context: ReactServerContext = { - $$typeof: REACT_CONTEXT_TYPE, + $$typeof: REACT_SERVER_CONTEXT_TYPE, // As a workaround to support multiple concurrent renderers, we categorize // some renderers as primary and others as secondary. We only expect // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - _currentValue: defaultValue, - _currentValue2: defaultValue, + _currentValue: (defaultValue: any), + _currentValue2: (defaultValue: any), // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. _threadCount: 0, + _definitionLoaded: false, // These are circular Provider: (null: any), displayName: globalName, @@ -55,9 +79,14 @@ export function createServerContext( return context; } -export function getOrCreateContextByName(name: string) { - if (!globalRegistry[name]) { - globalRegistry[name] = createServerContext(name, null); +// This function is called by FlightClient to create a server context sent from +// the server. Its possible that FlightClient is creating it before the +// definition is loaded on the server. We'll create it with a null default value +// if thats the case and when the definition loads it will set the correct +// default value. +export function getOrCreateServerContext(globalName: string) { + if (!globalRegistry[globalName]) { + globalRegistry[globalName] = _createServerContext(globalName, undefined); } - return globalRegistry[name]; + return globalRegistry[globalName]; } diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index d33d2a92d29..ff488467f04 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -247,6 +247,7 @@ export const enableUpdaterTracking = __PROFILE__; // Only enabled in RN, related to enableComponentStackLocations export const disableNativeComponentFrames = false; +export const enableServerContext = false; // Internal only. export const enableGetInspectorDataForInstanceInProduction = false; diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index 299869d12e8..01924071581 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -19,6 +19,7 @@ export const REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode'); export const REACT_PROFILER_TYPE = Symbol.for('react.profiler'); export const REACT_PROVIDER_TYPE = Symbol.for('react.provider'); export const REACT_CONTEXT_TYPE = Symbol.for('react.context'); +export const REACT_SERVER_CONTEXT_TYPE = Symbol.for('react.server_context'); export const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref'); export const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense'); export const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list'); diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 2282c05c4b2..70bd32f19c8 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -25,7 +25,7 @@ export type ReactText = string | number; export type ReactProvider = { $$typeof: Symbol | number, - type: ReactProviderType, + type: ReactProviderType | ReactServerProviderType, key: null | string, ref: null, props: { @@ -92,6 +92,7 @@ export type ReactServerContext = { _currentRenderer?: Object | null, _currentRenderer2?: Object | null, _threadCount: number, + _definitionLoaded: boolean, +displayName: string, ... }; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 1eb2086c896..f44819224b2 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -74,6 +74,8 @@ export const allowConcurrentByDefault = true; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = false; + export const enableUseMutableSource = true; export const enableTransitionTracing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index e9850c400a9..25e21000ac4 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -66,6 +66,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = false; export const enableUseMutableSource = false; export const enableTransitionTracing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index cd488a2b7e5..88444024122 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -66,6 +66,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = true; export const enableUseMutableSource = false; export const enableTransitionTracing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 6d1dcfa842c..5d75422fd2c 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -66,6 +66,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = false; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index e6c46ee341f..a9ccb8f313c 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -66,6 +66,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = true; export const enableUseMutableSource = false; export const enableTransitionTracing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index 0ae82026016..c965a21e6bc 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -66,7 +66,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; - +export const enableServerContext = false; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index cf61f17054d..a978d016310 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -101,6 +101,7 @@ export const deletedTreeCleanUpLevel = 3; export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = true; +export const enableServerContext = false; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 907f34d4cae..9a1cee36a13 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -8,7 +8,11 @@ */ import type {LazyComponent} from 'react/src/ReactLazy'; -import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; +import type { + ReactContext, + ReactServerContext, + ReactProviderType, +} from 'shared/ReactTypes'; import { REACT_CONTEXT_TYPE, diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 66feddb5c01..18b2746c0e5 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -412,5 +412,6 @@ "424": "Text content does not match server-rendered HTML.", "425": "A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition.", "426": "An error occurred during hydration. The server HTML was replaced with client content", - "427": "useServerContext is only supported while rendering." + "427": "useServerContext is only supported while rendering.", + "428": "ServerContext: %s already defined" } From 905d184ed14395237bb814ede29b830fab11446c Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 14 Feb 2022 10:50:40 -0500 Subject: [PATCH 33/83] flow again =) --- packages/react-reconciler/src/ReactFiberHooks.new.js | 2 +- packages/react-reconciler/src/ReactFiberHooks.old.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index f5786784797..eeb26ddda31 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -3160,7 +3160,7 @@ if (__DEV__) { }; } if (enableServerContext) { - HooksDispatcherOnRerenderInDEV.useServerContext = updateServerContext; + (HooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = updateServerContext; } InvalidNestedHooksDispatcherOnMountInDEV = { diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 27cb6e4551c..31dae9464d9 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -3160,7 +3160,7 @@ if (__DEV__) { }; } if (enableServerContext) { - HooksDispatcherOnRerenderInDEV.useServerContext = updateServerContext; + (HooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = updateServerContext; } InvalidNestedHooksDispatcherOnMountInDEV = { From 42ca7985e7b8c5a5600bc177b3a6ae5124f28072 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Tue, 15 Feb 2022 13:53:17 -0500 Subject: [PATCH 34/83] duplicate ReactServerContext across packages --- .../react-client/src/ReactFlightClient.js | 2 +- .../src/ReactFlightClientStream.js | 1 + .../react-client/src/ReactServerContext.js | 99 +++++++++++++++++++ .../src/__tests__/ReactFlight-test.js | 7 ++ .../src/ReactFiberBeginWork.new.js | 1 + .../react-server/src/ReactFlightServer.js | 2 +- .../react-server/src/ReactServerContext.js | 98 ++++++++++++++++++ packages/react/src/ReactServerContext.js | 27 +++-- .../react/src/ReactServerContextRegistry.js | 5 + packages/react/src/ReactSharedInternals.js | 6 ++ .../shared/forks/ReactFeatureFlags.www.js | 2 +- 11 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 packages/react-client/src/ReactServerContext.js create mode 100644 packages/react-server/src/ReactServerContext.js create mode 100644 packages/react/src/ReactServerContextRegistry.js diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index e43446435d5..90a960e3a61 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -24,7 +24,7 @@ import { parseModel, } from './ReactFlightClientHostConfig'; -import {getOrCreateServerContext} from 'react/src/ReactServerContext'; +import {getOrCreateServerContext} from './ReactServerContext'; import { REACT_LAZY_TYPE, diff --git a/packages/react-client/src/ReactFlightClientStream.js b/packages/react-client/src/ReactFlightClientStream.js index 9f07d8cc999..e54f2adb69e 100644 --- a/packages/react-client/src/ReactFlightClientStream.js +++ b/packages/react-client/src/ReactFlightClientStream.js @@ -46,6 +46,7 @@ function processFullRow(response: Response, row: string): void { return; } case 'M': { + debugger; resolveModule(response, id, text); return; } diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js new file mode 100644 index 00000000000..42639302ac3 --- /dev/null +++ b/packages/react-client/src/ReactServerContext.js @@ -0,0 +1,99 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import { + REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, +} from 'shared/ReactSymbols'; + +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; + +import ReactSharedInternals from 'shared/ReactSharedInternals'; + +import {enableServerContext} from 'shared/ReactFeatureFlags'; + +const globalServerContextRegistry = + ReactSharedInternals.globalServerContextRegistry; + +export function createServerContext( + globalName: string, + defaultValue: T, +): ReactServerContext { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + defaultValue, + ); + } + const context = globalServerContextRegistry[globalName]; + if (!context._definitionLoaded) { + context._currentValue = defaultValue; + context._currentValue2 = defaultValue; + context._definitionLoaded = true; + } else { + throw new Error(`ServerContext: ${globalName} already defined`); + } + return context; +} + +function _createServerContext( + globalName: string, + defaultValue?: T, +): ReactServerContext { + const context: ReactServerContext = { + $$typeof: REACT_SERVER_CONTEXT_TYPE, + // As a workaround to support multiple concurrent renderers, we categorize + // some renderers as primary and others as secondary. We only expect + // there to be two concurrent renderers at most: React Native (primary) and + // Fabric (secondary); React DOM (primary) and React ART (secondary). + // Secondary renderers store their context values on separate fields. + _currentValue: (defaultValue: any), + _currentValue2: (defaultValue: any), + // Used to track how many concurrent renderers this context currently + // supports within in a single renderer. Such as parallel server rendering. + _threadCount: 0, + _definitionLoaded: false, + // These are circular + Provider: (null: any), + displayName: globalName, + }; + + context.Provider = { + $$typeof: REACT_PROVIDER_TYPE, + _context: context, + }; + + if (__DEV__) { + context._currentRenderer = null; + context._currentRenderer2 = null; + } + globalServerContextRegistry[globalName] = context; + return context; +} + +// This function is called by FlightClient to create a server context sent from +// the server. Its possible that FlightClient is creating it before the +// definition is loaded on the server. We'll create it with a null default value +// if thats the case and when the definition loads it will set the correct +// default value. +export function getOrCreateServerContext(globalName: string) { + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + undefined, + ); + } + return globalServerContextRegistry[globalName]; +} diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index d86e9cad522..683e4c2a3e2 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -505,6 +505,13 @@ describe('ReactFlight', () => { expect(Scheduler).toHaveYielded(['ClientBar']); expect(ReactNoop).toMatchRenderedOutput(hi this is server); + + expect(() => { + const ServerContext2 = React.createServerContext( + 'ServerContext', + 'default hello from server', + ); + }).toThrow('ServerContext: ServerContext already defined'); }); // @gate enableServerContext diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index fef0efef078..d29510d898c 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -1551,6 +1551,7 @@ function mountLazyComponent( hint = ' Did you wrap a component in React.lazy() more than once?'; } } + debugger; // This message intentionally doesn't mention ForwardRef or MemoComponent // because the fact that it's a separate type of work is an diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 911f28a2300..33d3d16fa68 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -38,7 +38,7 @@ import { isModuleReference, } from './ReactFlightServerConfig'; -import {getOrCreateServerContext} from 'react/src/ReactServerContext'; +import {getOrCreateServerContext} from './ReactServerContext'; import { Dispatcher, getCurrentCache, diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js new file mode 100644 index 00000000000..b9e48c085ad --- /dev/null +++ b/packages/react-server/src/ReactServerContext.js @@ -0,0 +1,98 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import { + REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, +} from 'shared/ReactSymbols'; + +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; + +import {enableServerContext} from 'shared/ReactFeatureFlags'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; + +const globalServerContextRegistry = + ReactSharedInternals.globalServerContextRegistry; + +export function createServerContext( + globalName: string, + defaultValue: T, +): ReactServerContext { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + defaultValue, + ); + } + const context = globalServerContextRegistry[globalName]; + if (!context._definitionLoaded) { + context._currentValue = defaultValue; + context._currentValue2 = defaultValue; + context._definitionLoaded = true; + } else { + throw new Error(`ServerContext: ${globalName} already defined`); + } + return context; +} + +function _createServerContext( + globalName: string, + defaultValue?: T, +): ReactServerContext { + const context: ReactServerContext = { + $$typeof: REACT_SERVER_CONTEXT_TYPE, + // As a workaround to support multiple concurrent renderers, we categorize + // some renderers as primary and others as secondary. We only expect + // there to be two concurrent renderers at most: React Native (primary) and + // Fabric (secondary); React DOM (primary) and React ART (secondary). + // Secondary renderers store their context values on separate fields. + _currentValue: (defaultValue: any), + _currentValue2: (defaultValue: any), + // Used to track how many concurrent renderers this context currently + // supports within in a single renderer. Such as parallel server rendering. + _threadCount: 0, + _definitionLoaded: false, + // These are circular + Provider: (null: any), + displayName: globalName, + }; + + context.Provider = { + $$typeof: REACT_PROVIDER_TYPE, + _context: context, + }; + + if (__DEV__) { + context._currentRenderer = null; + context._currentRenderer2 = null; + } + globalServerContextRegistry[globalName] = context; + return context; +} + +// This function is called by FlightClient to create a server context sent from +// the server. Its possible that FlightClient is creating it before the +// definition is loaded on the server. We'll create it with a null default value +// if thats the case and when the definition loads it will set the correct +// default value. +export function getOrCreateServerContext(globalName: string) { + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + undefined, + ); + } + return globalServerContextRegistry[globalName]; +} diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 1d15a9b3161..42639302ac3 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -17,11 +17,12 @@ import type { ServerContextJSONValue, } from 'shared/ReactTypes'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; + import {enableServerContext} from 'shared/ReactFeatureFlags'; -const globalRegistry: { - [globalName: string]: ReactServerContext, -} = {}; +const globalServerContextRegistry = + ReactSharedInternals.globalServerContextRegistry; export function createServerContext( globalName: string, @@ -30,10 +31,13 @@ export function createServerContext( if (!enableServerContext) { throw new Error('Not implemented.'); } - if (!globalRegistry[globalName]) { - globalRegistry[globalName] = _createServerContext(globalName, defaultValue); + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + defaultValue, + ); } - const context = globalRegistry[globalName]; + const context = globalServerContextRegistry[globalName]; if (!context._definitionLoaded) { context._currentValue = defaultValue; context._currentValue2 = defaultValue; @@ -75,7 +79,7 @@ function _createServerContext( context._currentRenderer = null; context._currentRenderer2 = null; } - globalRegistry[globalName] = context; + globalServerContextRegistry[globalName] = context; return context; } @@ -85,8 +89,11 @@ function _createServerContext( // if thats the case and when the definition loads it will set the correct // default value. export function getOrCreateServerContext(globalName: string) { - if (!globalRegistry[globalName]) { - globalRegistry[globalName] = _createServerContext(globalName, undefined); + if (!globalServerContextRegistry[globalName]) { + globalServerContextRegistry[globalName] = _createServerContext( + globalName, + undefined, + ); } - return globalRegistry[globalName]; + return globalServerContextRegistry[globalName]; } diff --git a/packages/react/src/ReactServerContextRegistry.js b/packages/react/src/ReactServerContextRegistry.js new file mode 100644 index 00000000000..c98f12b6e35 --- /dev/null +++ b/packages/react/src/ReactServerContextRegistry.js @@ -0,0 +1,5 @@ +import type {ReactServerContext} from 'shared/ReactTypes'; + +export const globalServerContextRegistry: { + [globalName: string]: ReactServerContext, +} = {}; diff --git a/packages/react/src/ReactSharedInternals.js b/packages/react/src/ReactSharedInternals.js index 2874b03985b..1f7debf27b7 100644 --- a/packages/react/src/ReactSharedInternals.js +++ b/packages/react/src/ReactSharedInternals.js @@ -10,6 +10,8 @@ import ReactCurrentBatchConfig from './ReactCurrentBatchConfig'; import ReactCurrentActQueue from './ReactCurrentActQueue'; import ReactCurrentOwner from './ReactCurrentOwner'; import ReactDebugCurrentFrame from './ReactDebugCurrentFrame'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; +import {globalServerContextRegistry} from './ReactServerContextRegistry'; const ReactSharedInternals = { ReactCurrentDispatcher, @@ -22,4 +24,8 @@ if (__DEV__) { ReactSharedInternals.ReactCurrentActQueue = ReactCurrentActQueue; } +if (enableServerContext) { + ReactSharedInternals.globalServerContextRegistry = globalServerContextRegistry; +} + export default ReactSharedInternals; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index a978d016310..9c73333bfd1 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -101,7 +101,7 @@ export const deletedTreeCleanUpLevel = 3; export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = true; -export const enableServerContext = false; +export const enableServerContext = true; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; From 44572956a4cac55b6f74227b6e2b5f0f18bac053 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 20:10:07 -0500 Subject: [PATCH 35/83] store default value when lazily initializing server context --- .../src/ReactFlightClientStream.js | 1 - .../react-client/src/ReactServerContext.js | 55 ++++++-- .../src/__tests__/ReactFlight-test.js | 117 ++++++++++++++++-- .../src/ReactFiberBeginWork.new.js | 1 - .../react-server/src/ReactFlightServer.js | 18 +-- .../react-server/src/ReactServerContext.js | 58 +++++++-- packages/react/src/ReactServerContext.js | 55 ++++++-- .../react/src/ReactServerContextRegistry.js | 4 +- packages/shared/ReactTypes.js | 3 + 9 files changed, 265 insertions(+), 47 deletions(-) diff --git a/packages/react-client/src/ReactFlightClientStream.js b/packages/react-client/src/ReactFlightClientStream.js index e54f2adb69e..9f07d8cc999 100644 --- a/packages/react-client/src/ReactFlightClientStream.js +++ b/packages/react-client/src/ReactFlightClientStream.js @@ -46,7 +46,6 @@ function processFullRow(response: Response, row: string): void { return; } case 'M': { - debugger; resolveModule(response, id, text); return; } diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index 42639302ac3..8d4d6a940fc 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -24,6 +24,8 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; +const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; + export function createServerContext( globalName: string, defaultValue: T, @@ -39,9 +41,8 @@ export function createServerContext( } const context = globalServerContextRegistry[globalName]; if (!context._definitionLoaded) { - context._currentValue = defaultValue; - context._currentValue2 = defaultValue; context._definitionLoaded = true; + context._defaultValue = defaultValue; } else { throw new Error(`ServerContext: ${globalName} already defined`); } @@ -50,7 +51,7 @@ export function createServerContext( function _createServerContext( globalName: string, - defaultValue?: T, + defaultValue: T, ): ReactServerContext { const context: ReactServerContext = { $$typeof: REACT_SERVER_CONTEXT_TYPE, @@ -59,8 +60,48 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - _currentValue: (defaultValue: any), - _currentValue2: (defaultValue: any), + __currentValue: defaultValue, + __currentValue2: defaultValue, + + get _currentValue() { + const value = context.__currentValue; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + } + return value; + }, + + set _currentValue(value) { + context.__currentValue = value; + }, + + get _currentValue2() { + const value = context.__currentValue2; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + return (undefined: any); + } + return value; + }, + + set _currentValue2(value) { + context.__currentValue2 = value; + }, + + _defaultValue: (undefined: any), + // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. _threadCount: 0, @@ -88,11 +129,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string) { +export function getOrCreateServerContext(globalName: string, value: any) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - undefined, + value === undefined ? DEFAULT_PLACEHOLDER : value, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 683e4c2a3e2..dae8d4d5858 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -18,6 +18,7 @@ let ReactNoopFlightClient; let ErrorBoundary; let NoErrorExpected; let Scheduler; +let globalServerContextRegistry; describe('ReactFlight', () => { beforeEach(() => { @@ -29,6 +30,9 @@ describe('ReactFlight', () => { ReactNoopFlightClient = require('react-noop-renderer/flight-client'); act = require('jest-react').act; Scheduler = require('scheduler'); + const ReactSharedInternals = require('shared/ReactSharedInternals').default; + globalServerContextRegistry = + ReactSharedInternals.globalServerContextRegistry; ErrorBoundary = class extends React.Component { state = {hasError: false, error: null}; @@ -331,7 +335,7 @@ describe('ReactFlight', () => { it('propagates ServerContext providers in flight', () => { const ServerContext = React.createServerContext( 'ServerContext', - 'default hello from server', + 'default', ); function Foo() { @@ -362,7 +366,7 @@ describe('ReactFlight', () => { it('propagates ServerContext and cleansup providers in flight', () => { const ServerContext = React.createServerContext( 'ServerContext', - 'default hello from server', + 'default', ); function Foo() { @@ -402,7 +406,7 @@ describe('ReactFlight', () => { hi this is server2 hi this is server outer hi this is server outer2 - default hello from server + default , ); }); @@ -411,7 +415,7 @@ describe('ReactFlight', () => { it('propagates ServerContext providers in flight after suspending', async () => { const ServerContext = React.createServerContext( 'ServerContext', - 'default hello from server', + 'default', ); function Foo() { @@ -469,7 +473,7 @@ describe('ReactFlight', () => { it('serializes ServerContext to client', async () => { const ServerContext = React.createServerContext( 'ServerContext', - 'default hello from server', + 'default', ); function ClientBar() { @@ -507,10 +511,7 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(hi this is server); expect(() => { - const ServerContext2 = React.createServerContext( - 'ServerContext', - 'default hello from server', - ); + React.createServerContext('ServerContext', 'default'); }).toThrow('ServerContext: ServerContext already defined'); }); @@ -518,7 +519,7 @@ describe('ReactFlight', () => { it('takes ServerContext from client for refetching usecases', async () => { const ServerContext = React.createServerContext( 'ServerContext', - 'default hello from server', + 'default', ); function Bar() { return {React.useServerContext(ServerContext)}; @@ -539,5 +540,101 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(Override); }); + + // @gate enableServerContext + it('sets default initial value when defined lazily on server or client', async () => { + let ServerContext; + function inlineLazyServerContextInitialization() { + if (!ServerContext) { + ServerContext = React.createServerContext('ServerContext', 'default'); + } + return ServerContext; + } + + let ClientContext; + function inlineContextInitialization() { + if (!ClientContext) { + ClientContext = React.createServerContext('ServerContext', 'default'); + } + return ClientContext; + } + + function ClientBaz() { + const context = inlineContextInitialization(); + const value = React.useServerContext(context); + return
{value}
; + } + + const Baz = moduleReference(ClientBaz); + + function Bar() { + return ( +
+
+ {React.useServerContext(inlineLazyServerContextInitialization())} +
+ +
+ ); + } + + function ServerApp() { + const Context = inlineLazyServerContextInitialization(); + return ( + <> + + + + + + ); + } + + function ClientApp({serverModel}) { + return ( + <> + {serverModel} + + + ); + } + + const transport = ReactNoopFlightServer.render(, { + context: [ + { + name: 'ServerContext', + value: 'Override', + }, + ], + }); + + expect(ClientContext).toBe(undefined); + act(() => { + delete globalServerContextRegistry.ServerContext; + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + const serverModel = ReactNoopFlightClient.read(transport); + ReactNoop.render(); + }); + + expect(ReactNoop).toMatchRenderedOutput( + <> +
+
test
+
test
+
+
+
Override
+ + {/** In practice this would also be Override because the */} + {/** server context sent up to the server would be around this*/} + {/** tree. For this test we didn't do that though so it uses the */} + {/** real default */} +
default
+
+
default
+ , + ); + }); }); }); diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index d29510d898c..fef0efef078 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -1551,7 +1551,6 @@ function mountLazyComponent( hint = ' Did you wrap a component in React.lazy() more than once?'; } } - debugger; // This message intentionally doesn't mention ForwardRef or MemoComponent // because the fact that it's a separate type of work is an diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 33d3d16fa68..9e93c730643 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -209,7 +209,6 @@ function attemptResolveElement( return attemptResolveElement(type.type, key, ref, props); } case REACT_PROVIDER_TYPE: { - parentsWithContextStack.push(type._context); pushProvider(type._context, props.value); return [ REACT_PROVIDER_TYPE, @@ -420,15 +419,6 @@ function isReactElement(value: mixed) { ); } -// Save all of the parents/contexts as we recursively stringify onto this stack -// so that we can popContexts as we recurse into neighbors. -const parentsWithContextStack: Array< - | {+[key: string | number]: ReactModel} - | $ReadOnlyArray - | ReactServerContext - | ReactModel, -> = []; - export function resolveModelToJSON( request: Request, parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray, @@ -499,7 +489,11 @@ export function resolveModelToJSON( return null; } - if (value.$$typeof === REACT_SERVER_CONTEXT_TYPE && key === '4') { + if ( + value.$$typeof === REACT_SERVER_CONTEXT_TYPE && + key === '4' && + parent[0] === REACT_PROVIDER_TYPE + ) { popProvider((value: any)); return (undefined: any); } @@ -880,7 +874,7 @@ function importServerContexts( if (contexts) { for (let i = 0; i < contexts.length; i++) { const {name, value} = contexts[i]; - const context = getOrCreateServerContext(name); + const context = getOrCreateServerContext(name, value); pushProvider(context, value); registry[name] = context; } diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index b9e48c085ad..8d4d6a940fc 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -17,12 +17,15 @@ import type { ServerContextJSONValue, } from 'shared/ReactTypes'; -import {enableServerContext} from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; + const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; +const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; + export function createServerContext( globalName: string, defaultValue: T, @@ -38,9 +41,8 @@ export function createServerContext( } const context = globalServerContextRegistry[globalName]; if (!context._definitionLoaded) { - context._currentValue = defaultValue; - context._currentValue2 = defaultValue; context._definitionLoaded = true; + context._defaultValue = defaultValue; } else { throw new Error(`ServerContext: ${globalName} already defined`); } @@ -49,7 +51,7 @@ export function createServerContext( function _createServerContext( globalName: string, - defaultValue?: T, + defaultValue: T, ): ReactServerContext { const context: ReactServerContext = { $$typeof: REACT_SERVER_CONTEXT_TYPE, @@ -58,8 +60,48 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - _currentValue: (defaultValue: any), - _currentValue2: (defaultValue: any), + __currentValue: defaultValue, + __currentValue2: defaultValue, + + get _currentValue() { + const value = context.__currentValue; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + } + return value; + }, + + set _currentValue(value) { + context.__currentValue = value; + }, + + get _currentValue2() { + const value = context.__currentValue2; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + return (undefined: any); + } + return value; + }, + + set _currentValue2(value) { + context.__currentValue2 = value; + }, + + _defaultValue: (undefined: any), + // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. _threadCount: 0, @@ -87,11 +129,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string) { +export function getOrCreateServerContext(globalName: string, value: any) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - undefined, + value === undefined ? DEFAULT_PLACEHOLDER : value, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 42639302ac3..8d4d6a940fc 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -24,6 +24,8 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; +const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; + export function createServerContext( globalName: string, defaultValue: T, @@ -39,9 +41,8 @@ export function createServerContext( } const context = globalServerContextRegistry[globalName]; if (!context._definitionLoaded) { - context._currentValue = defaultValue; - context._currentValue2 = defaultValue; context._definitionLoaded = true; + context._defaultValue = defaultValue; } else { throw new Error(`ServerContext: ${globalName} already defined`); } @@ -50,7 +51,7 @@ export function createServerContext( function _createServerContext( globalName: string, - defaultValue?: T, + defaultValue: T, ): ReactServerContext { const context: ReactServerContext = { $$typeof: REACT_SERVER_CONTEXT_TYPE, @@ -59,8 +60,48 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - _currentValue: (defaultValue: any), - _currentValue2: (defaultValue: any), + __currentValue: defaultValue, + __currentValue2: defaultValue, + + get _currentValue() { + const value = context.__currentValue; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + } + return value; + }, + + set _currentValue(value) { + context.__currentValue = value; + }, + + get _currentValue2() { + const value = context.__currentValue2; + if (value === DEFAULT_PLACEHOLDER) { + // If there is an entry in defaults then the definition was loaded + // and we should use the default value in the definition. + // Otherwise the definition hasn't loaded so `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + if ('_defaultValue' in context) { + return (context: any)._defaultValue; + } + return (undefined: any); + } + return value; + }, + + set _currentValue2(value) { + context.__currentValue2 = value; + }, + + _defaultValue: (undefined: any), + // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. _threadCount: 0, @@ -88,11 +129,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string) { +export function getOrCreateServerContext(globalName: string, value: any) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - undefined, + value === undefined ? DEFAULT_PLACEHOLDER : value, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react/src/ReactServerContextRegistry.js b/packages/react/src/ReactServerContextRegistry.js index c98f12b6e35..f24d9474309 100644 --- a/packages/react/src/ReactServerContextRegistry.js +++ b/packages/react/src/ReactServerContextRegistry.js @@ -2,4 +2,6 @@ import type {ReactServerContext} from 'shared/ReactTypes'; export const globalServerContextRegistry: { [globalName: string]: ReactServerContext, -} = {}; +} = { + __defaultValue: {}, +}; diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 70bd32f19c8..6625b5bf850 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -87,6 +87,9 @@ export type ServerContextJSONValue = export type ReactServerContext = { $$typeof: Symbol | number, Provider: ReactServerProviderType, + _defaultValue: T, + __currentValue: T, + __currentValue2: T, _currentValue: T, _currentValue2: T, _currentRenderer?: Object | null, From a69065bc8f2b562df7d447c2c9c201cf97c11ed9 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 20:15:38 -0500 Subject: [PATCH 36/83] . --- packages/react-client/src/ReactServerContext.js | 2 +- packages/react-server/src/ReactServerContext.js | 2 +- packages/react/src/ReactServerContext.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index 8d4d6a940fc..2cab7790b5f 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -70,7 +70,7 @@ function _createServerContext( // and we should use the default value in the definition. // Otherwise the definition hasn't loaded so `useServerContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; } } diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index 8d4d6a940fc..2cab7790b5f 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -70,7 +70,7 @@ function _createServerContext( // and we should use the default value in the definition. // Otherwise the definition hasn't loaded so `useServerContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; } } diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 8d4d6a940fc..2cab7790b5f 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -70,7 +70,7 @@ function _createServerContext( // and we should use the default value in the definition. // Otherwise the definition hasn't loaded so `useServerContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; } } From ef20b4041091b09c6a2f69d8fd37ad77ea07bffd Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 20:19:31 -0500 Subject: [PATCH 37/83] better comment --- .../react-client/src/ReactServerContext.js | 18 +++++++++--------- .../react-server/src/ReactServerContext.js | 18 +++++++++--------- packages/react/src/ReactServerContext.js | 18 +++++++++--------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index 2cab7790b5f..a5064da544f 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -66,12 +66,12 @@ function _createServerContext( get _currentValue() { const value = context.__currentValue; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } } return value; @@ -84,12 +84,12 @@ function _createServerContext( get _currentValue2() { const value = context.__currentValue2; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } return (undefined: any); } diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index 2cab7790b5f..a5064da544f 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -66,12 +66,12 @@ function _createServerContext( get _currentValue() { const value = context.__currentValue; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } } return value; @@ -84,12 +84,12 @@ function _createServerContext( get _currentValue2() { const value = context.__currentValue2; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } return (undefined: any); } diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 2cab7790b5f..a5064da544f 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -66,12 +66,12 @@ function _createServerContext( get _currentValue() { const value = context.__currentValue; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } } return value; @@ -84,12 +84,12 @@ function _createServerContext( get _currentValue2() { const value = context.__currentValue2; if (value === DEFAULT_PLACEHOLDER) { - // If there is an entry in defaults then the definition was loaded - // and we should use the default value in the definition. - // Otherwise the definition hasn't loaded so `useServerContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - if ('_defaultValue' in context) { + if (context._definitionLoaded) { return (context: any)._defaultValue; + } else { + // If the definition hasn't loaded then we know `useServerContext` is not + // being called, in this case we'll just return the DEFAULT_PLACEHOLDER + // because it won't actually be returned to a component } return (undefined: any); } From c5a58d31e77f69728b928891db36f8eff5bb804e Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 20:42:02 -0500 Subject: [PATCH 38/83] derp... missing import --- packages/react-client/src/ReactServerContext.js | 5 ++++- packages/react-server/src/ReactServerContext.js | 5 ++++- packages/react/src/ReactServerContext.js | 5 ++++- packages/react/src/forks/ReactSharedInternals.umd.js | 6 ++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index a5064da544f..caa3cb381c2 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -24,7 +24,10 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +let DEFAULT_PLACEHOLDER; +if (enableServerContext) { + DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +} export function createServerContext( globalName: string, diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index a5064da544f..caa3cb381c2 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -24,7 +24,10 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +let DEFAULT_PLACEHOLDER; +if (enableServerContext) { + DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +} export function createServerContext( globalName: string, diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index a5064da544f..caa3cb381c2 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -24,7 +24,10 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -const DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +let DEFAULT_PLACEHOLDER; +if (enableServerContext) { + DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; +} export function createServerContext( globalName: string, diff --git a/packages/react/src/forks/ReactSharedInternals.umd.js b/packages/react/src/forks/ReactSharedInternals.umd.js index 04e8cb577f7..a41c572462b 100644 --- a/packages/react/src/forks/ReactSharedInternals.umd.js +++ b/packages/react/src/forks/ReactSharedInternals.umd.js @@ -11,6 +11,8 @@ import ReactCurrentActQueue from '../ReactCurrentActQueue'; import ReactCurrentOwner from '../ReactCurrentOwner'; import ReactDebugCurrentFrame from '../ReactDebugCurrentFrame'; import ReactCurrentBatchConfig from '../ReactCurrentBatchConfig'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; +import {globalServerContextRegistry} from '../ReactServerContextRegistry'; const ReactSharedInternals = { ReactCurrentDispatcher, @@ -30,4 +32,8 @@ if (__DEV__) { ReactSharedInternals.ReactDebugCurrentFrame = ReactDebugCurrentFrame; } +if (enableServerContext) { + ReactSharedInternals.globalServerContextRegistry = globalServerContextRegistry; +} + export default ReactSharedInternals; From 6124a345c98bb21c966dfe96efc5092d9dbab0d9 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 20:53:20 -0500 Subject: [PATCH 39/83] rm optional chaining --- packages/react-server/src/ReactFlightServer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 9e93c730643..6bf8f20eccf 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -131,7 +131,7 @@ export function createRequest( options?: RequestOptions, ): Request { const pingedSegments = []; - const onError = options?.onError; + const onError = options ? options.onError : undefined; const request = { status: OPEN, fatalError: null, @@ -152,7 +152,7 @@ export function createRequest( }, }; request.pendingChunks++; - const context = createRootContext(options?.context); + const context = createRootContext(options ? options.context : undefined); const rootSegment = createSegment(request, model, context); pingedSegments.push(rootSegment); return request; From fd1465e10e33e6b44d8ebedc08f582d6107f8683 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 16 Feb 2022 21:13:46 -0500 Subject: [PATCH 40/83] missed feature flag --- packages/shared/forks/ReactFeatureFlags.test-renderer.native.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index 54828d7ebd6..51015b109ab 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -64,6 +64,7 @@ export const allowConcurrentByDefault = true; export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = false; +export const enableServerContext = false; export const enableUseMutableSource = false; export const enableTransitionTracing = false; From ebe8a1efcfc3b01e8bf90b14171a23d33ea391b9 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 17 Feb 2022 07:03:49 -0500 Subject: [PATCH 41/83] React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED ?? --- packages/react-client/src/__tests__/ReactFlight-test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index dae8d4d5858..54fc5e5bb22 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -30,7 +30,8 @@ describe('ReactFlight', () => { ReactNoopFlightClient = require('react-noop-renderer/flight-client'); act = require('jest-react').act; Scheduler = require('scheduler'); - const ReactSharedInternals = require('shared/ReactSharedInternals').default; + const ReactSharedInternals = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; From 4e8a2d293ce0f5807aa3649e13530ec351e56341 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 17 Feb 2022 09:58:46 -0500 Subject: [PATCH 42/83] add warning if non ServerContext passed into useServerContext --- packages/react-debug-tools/src/ReactDebugHooks.js | 3 +-- packages/react-server/src/ReactFlightHooks.js | 11 ----------- packages/react-server/src/ReactFlightServer.js | 8 +------- packages/react/src/ReactHooks.js | 10 +++++++++- packages/shared/forks/ReactFeatureFlags.www.js | 2 +- scripts/error-codes/codes.json | 3 ++- 6 files changed, 14 insertions(+), 23 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 415d1d99e20..09b69e30c00 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -351,8 +351,7 @@ function useId(): string { const Dispatcher: DispatcherType = { getCacheForType, - // TODO: figure out why flow is complaining here - readContext: (readContext: any), + readContext, useCacheRefresh, useCallback, useContext, diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index b87b4b87732..97071a9b289 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -105,14 +105,3 @@ export function setCurrentCache(cache: Map | null) { export function getCurrentCache() { return currentCache; } - -type ServerContextCache = {[name: string]: ReactServerContext} | null; -let currentServerContexts: ServerContextCache = null; - -export function setCurrentServerContexts(contexts: ServerContextCache) { - currentServerContexts = contexts; -} - -export function getCurrentServerContexts(): ServerContextCache { - return currentServerContexts; -} diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 6bf8f20eccf..1f86dfd23c1 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -39,12 +39,7 @@ import { } from './ReactFlightServerConfig'; import {getOrCreateServerContext} from './ReactServerContext'; -import { - Dispatcher, - getCurrentCache, - setCurrentCache, - setCurrentServerContexts, -} from './ReactFlightHooks'; +import {Dispatcher, getCurrentCache, setCurrentCache} from './ReactFlightHooks'; import { pushProvider, popProvider, @@ -879,6 +874,5 @@ function importServerContexts( registry[name] = context; } } - setCurrentServerContexts(registry); return getActiveContext(); } diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index d200348c75d..2769f9e1a32 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -8,6 +8,8 @@ */ import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; +import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; import type { MutableSource, MutableSourceGetSnapshotFn, @@ -191,7 +193,13 @@ export function useMutableSource( export function useServerContext( Context: ReactServerContext, ): T { - // TODO: Warn if regular context is passed in + if (enableServerContext) { + if (Context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { + throw new Error( + 'useServerContext expects a context created with React.createServerContext', + ); + } + } const dispatcher = resolveDispatcher(); // $FlowFixMe This is unstable, thus optional return dispatcher.useServerContext(Context); diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 9c73333bfd1..a978d016310 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -101,7 +101,7 @@ export const deletedTreeCleanUpLevel = 3; export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = true; -export const enableServerContext = true; +export const enableServerContext = false; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 18b2746c0e5..c5fcac2d03b 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -413,5 +413,6 @@ "425": "A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition.", "426": "An error occurred during hydration. The server HTML was replaced with client content", "427": "useServerContext is only supported while rendering.", - "428": "ServerContext: %s already defined" + "428": "ServerContext: %s already defined", + "429": "useServerContext expects a context created with React.createServerContext" } From 72dbc55c53846326abfa7998da38cf5d982697c7 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 17 Feb 2022 11:32:07 -0500 Subject: [PATCH 43/83] pass context in as array of arrays --- .../react-client/src/__tests__/ReactFlight-test.js | 14 ++------------ .../src/ReactNoopFlightServer.js | 2 +- packages/react-server/src/ReactFlightServer.js | 10 ++++------ 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 54fc5e5bb22..1cf6f8f4d4f 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -526,12 +526,7 @@ describe('ReactFlight', () => { return {React.useServerContext(ServerContext)}; } const transport = ReactNoopFlightServer.render(, { - context: [ - { - name: 'ServerContext', - value: 'Override', - }, - ], + context: [['ServerContext', 'Override']], }); act(() => { @@ -601,12 +596,7 @@ describe('ReactFlight', () => { } const transport = ReactNoopFlightServer.render(, { - context: [ - { - name: 'ServerContext', - value: 'Override', - }, - ], + context: [['ServerContext', 'Override']], }); expect(ClientContext).toBe(undefined); diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index 5ef26f57813..15e991fa715 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -68,7 +68,7 @@ type ServerContextJSONValue = type Options = { onError?: (error: mixed) => void, - context?: Array<{name: string, value: ServerContextJSONValue}>, + context?: Array<[string, ServerContextJSONValue]>, }; function render(model: ReactModel, options?: Options): Destination { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 1f86dfd23c1..63b6737c7f2 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -106,7 +106,7 @@ export type Request = { export type RequestOptions = { onError?: (error: mixed) => void, - context?: Array<{name: string, value: ServerContextJSONValue}>, + context?: Array<[string, ServerContextJSONValue]>, }; const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; @@ -154,7 +154,7 @@ export function createRequest( } function createRootContext( - reqContext?: Array<{name: string, value: ServerContextJSONValue}>, + reqContext?: Array<[string, ServerContextJSONValue]>, ) { return importServerContexts(reqContext); } @@ -861,14 +861,12 @@ export function startFlowing(request: Request, destination: Destination): void { } function importServerContexts( - contexts: - | Array<{name: string, value: ServerContextJSONValue}> - | typeof undefined, + contexts?: Array<[string, ServerContextJSONValue]>, ) { const registry: {[name: string]: ReactServerContext} = {}; if (contexts) { for (let i = 0; i < contexts.length; i++) { - const {name, value} = contexts[i]; + const [name, value] = contexts[i]; const context = getOrCreateServerContext(name, value); pushProvider(context, value); registry[name] = context; From 6e4ed97098994b83987200c22d5501c9463a5fcc Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Tue, 22 Feb 2022 21:52:52 -0500 Subject: [PATCH 44/83] make importServerContext nott pollute the global context state --- packages/react-server/src/ReactFlightServer.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 63b6737c7f2..c40e4a891de 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -45,6 +45,7 @@ import { popProvider, switchContext, getActiveContext, + rootContextSnapshot, } from './ReactFlightNewContext'; import { @@ -863,6 +864,8 @@ export function startFlowing(request: Request, destination: Destination): void { function importServerContexts( contexts?: Array<[string, ServerContextJSONValue]>, ) { + const prevContext = getActiveContext(); + switchContext(rootContextSnapshot); const registry: {[name: string]: ReactServerContext} = {}; if (contexts) { for (let i = 0; i < contexts.length; i++) { @@ -872,5 +875,7 @@ function importServerContexts( registry[name] = context; } } - return getActiveContext(); + const importedContext = getActiveContext(); + switchContext(prevContext); + return importedContext; } From c5988f2e98c1150d162308c522851457c725f283 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Tue, 22 Feb 2022 21:58:13 -0500 Subject: [PATCH 45/83] merge main --- scripts/error-codes/codes.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index c5fcac2d03b..765824d8931 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -403,16 +403,16 @@ "415": "Error parsing the data. It's probably an error code or network corruption.", "416": "This environment don't support binary chunks.", "417": "React currently only supports piping to one writable stream.", - "418": "Hydration failed because the initial UI does not match what was rendered on the server.", - "419": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", - "420": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", - "421": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", - "422": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", - "423": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", - "424": "Text content does not match server-rendered HTML.", - "425": "A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition.", - "426": "An error occurred during hydration. The server HTML was replaced with client content", - "427": "useServerContext is only supported while rendering.", - "428": "ServerContext: %s already defined", - "429": "useServerContext expects a context created with React.createServerContext" + "418": "An error occurred during hydration. The server HTML was replaced with client content", + "419": "useServerContext is only supported while rendering.", + "420": "ServerContext: %s already defined", + "421": "useServerContext expects a context created with React.createServerContext", + "422": "Hydration failed because the initial UI does not match what was rendered on the server.", + "423": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", + "424": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", + "425": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", + "426": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", + "427": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", + "428": "useServerContext expects a context created with React.createServerContext" + } From 5397db6b5a31a443faf63692fc1e3fd32b6921e1 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 23 Feb 2022 14:25:27 -0500 Subject: [PATCH 46/83] remove useServerContext --- .../react-client/src/ReactServerContext.js | 4 +- .../src/__tests__/ReactFlight-test.js | 16 +-- .../react-debug-tools/src/ReactDebugHooks.js | 12 --- .../src/server/ReactPartialRendererHooks.js | 15 --- .../src/ReactFiberHooks.new.js | 99 +------------------ .../src/ReactFiberHooks.old.js | 99 +------------------ .../src/ReactInternalTypes.js | 7 +- packages/react-server/src/ReactFizzHooks.js | 12 --- packages/react-server/src/ReactFlightHooks.js | 8 -- .../react-server/src/ReactServerContext.js | 4 +- .../src/ReactSuspenseTestUtils.js | 1 - packages/react/index.classic.fb.js | 1 - packages/react/index.experimental.js | 1 - packages/react/index.js | 1 - packages/react/index.modern.fb.js | 1 - packages/react/index.stable.js | 1 - packages/react/src/React.js | 2 - packages/react/src/ReactHooks.js | 17 ---- packages/react/src/ReactServerContext.js | 4 +- scripts/error-codes/codes.json | 20 ++-- 20 files changed, 26 insertions(+), 299 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index caa3cb381c2..fdb80d1f316 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -72,7 +72,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } @@ -90,7 +90,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 1cf6f8f4d4f..1dc4f6f8db8 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -318,7 +318,7 @@ describe('ReactFlight', () => { 'hello from server', ); function Foo() { - const context = React.useServerContext(ServerContext); + const context = React.useContext(ServerContext); return
{context}
; } @@ -349,7 +349,7 @@ describe('ReactFlight', () => { ); } function Bar() { - const context = React.useServerContext(ServerContext); + const context = React.useContext(ServerContext); return context; } @@ -390,7 +390,7 @@ describe('ReactFlight', () => { ); } function Bar() { - const context = React.useServerContext(ServerContext); + const context = React.useContext(ServerContext); return {context}; } @@ -445,7 +445,7 @@ describe('ReactFlight', () => { throw promise; } Scheduler.unstable_yieldValue('rendered'); - const context = React.useServerContext(ServerContext); + const context = React.useContext(ServerContext); return context; } @@ -479,7 +479,7 @@ describe('ReactFlight', () => { function ClientBar() { Scheduler.unstable_yieldValue('ClientBar'); - const context = React.useServerContext(ServerContext); + const context = React.useContext(ServerContext); return {context}; } @@ -523,7 +523,7 @@ describe('ReactFlight', () => { 'default', ); function Bar() { - return {React.useServerContext(ServerContext)}; + return {React.useContext(ServerContext)}; } const transport = ReactNoopFlightServer.render(, { context: [['ServerContext', 'Override']], @@ -557,7 +557,7 @@ describe('ReactFlight', () => { function ClientBaz() { const context = inlineContextInitialization(); - const value = React.useServerContext(context); + const value = React.useContext(context); return
{value}
; } @@ -567,7 +567,7 @@ describe('ReactFlight', () => { return (
- {React.useServerContext(inlineLazyServerContextInitialization())} + {React.useContext(inlineLazyServerContextInitialization())}
diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 09b69e30c00..a651dde86e5 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -123,17 +123,6 @@ function useContext(context: ReactContext): T { return context._currentValue; } -function useServerContext( - context: ReactServerContext, -): T { - hookLog.push({ - primitive: 'ServerContext', - stackError: new Error(), - value: context._currentValue, - }); - return context._currentValue; -} - function useState( initialState: (() => S) | S, ): [S, Dispatch>] { @@ -363,7 +352,6 @@ const Dispatcher: DispatcherType = { useMemo, useReducer, useRef, - useServerContext, useState, useTransition, useMutableSource, diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index b36cd289caa..1cd95f08fb6 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -14,8 +14,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, - ServerContextJSONValue, } from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; @@ -252,18 +250,6 @@ function useContext(context: ReactContext): T { return context[threadID]; } -function useServerContext( - context: ReactServerContext, -): T { - if (__DEV__) { - currentHookNameInDev = 'useContext'; - } - resolveCurrentlyRenderingComponent(); - const threadID = currentPartialRenderer.threadID; - validateContextBounds(context, threadID); - return context[threadID]; -} - function basicStateReducer(state: S, action: BasicStateAction): S { // $FlowFixMe: Flow doesn't like mixed types return typeof action === 'function' ? action(state) : action; @@ -547,7 +533,6 @@ export const Dispatcher: DispatcherType = { useReducer, useRef, useState, - useServerContext, useInsertionEffect, useLayoutEffect, useCallback, diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index eeb26ddda31..ed81d021377 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -2399,28 +2399,6 @@ function getCacheForType(resourceType: () => T): T { return cacheForType; } -function mountServerContext( - context: ReactServerContext, -): T { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - mountHookTypesDev(); - return readContext(context); -} - -function updateServerContext( - context: ReactServerContext, -): T { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); -} - export const ContextOnlyDispatcher: Dispatcher = { readContext, @@ -2449,10 +2427,6 @@ if (enableCache) { (ContextOnlyDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError; } -if (enableServerContext) { - (ContextOnlyDispatcher: Dispatcher).useServerContext = throwInvalidHookError; -} - const HooksDispatcherOnMount: Dispatcher = { readContext, @@ -2480,10 +2454,6 @@ if (enableCache) { (HooksDispatcherOnMount: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnMount: Dispatcher).useCacheRefresh = mountRefresh; } -if (enableServerContext) { - (HooksDispatcherOnMount: Dispatcher).useServerContext = readContext; -} - const HooksDispatcherOnUpdate: Dispatcher = { readContext, @@ -2511,9 +2481,6 @@ if (enableCache) { (HooksDispatcherOnUpdate: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnUpdate: Dispatcher).useCacheRefresh = updateRefresh; } -if (enableServerContext) { - (HooksDispatcherOnUpdate: Dispatcher).useServerContext = readContext; -} const HooksDispatcherOnRerender: Dispatcher = { readContext, @@ -2542,9 +2509,6 @@ if (enableCache) { (HooksDispatcherOnRerender: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnRerender: Dispatcher).useCacheRefresh = updateRefresh; } -if (enableServerContext) { - (HooksDispatcherOnRerender: Dispatcher).useServerContext = readContext; -} let HooksDispatcherOnMountInDEV: Dispatcher | null = null; let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null; @@ -2720,9 +2684,6 @@ if (__DEV__) { return mountRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnMountInDEV: Dispatcher).useServerContext = mountServerContext; - } HooksDispatcherOnMountWithHookTypesInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2865,9 +2826,6 @@ if (__DEV__) { return mountRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useServerContext = updateServerContext; - } HooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3010,12 +2968,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = updateServerContext; - } - - if (!enableServerContext) { - } HooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3159,9 +3111,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = updateServerContext; - } InvalidNestedHooksDispatcherOnMountInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3317,24 +3266,8 @@ if (__DEV__) { (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).getCacheForType = getCacheForType; (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() { currentHookNameInDev = 'useCacheRefresh'; - updateHookTypesDev(); - return mountRefresh(); - }; - } - - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); mountHookTypesDev(); - return readContext(context); + return mountRefresh(); }; } @@ -3496,21 +3429,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }; - } InvalidNestedHooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3671,19 +3589,4 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }; - } } diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 31dae9464d9..21e93682fe2 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -2399,28 +2399,6 @@ function getCacheForType(resourceType: () => T): T { return cacheForType; } -function mountServerContext( - context: ReactServerContext, -): T { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - mountHookTypesDev(); - return readContext(context); -} - -function updateServerContext( - context: ReactServerContext, -): T { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - updateHookTypesDev(); - return readContext(context); -} - export const ContextOnlyDispatcher: Dispatcher = { readContext, @@ -2449,10 +2427,6 @@ if (enableCache) { (ContextOnlyDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError; } -if (enableServerContext) { - (ContextOnlyDispatcher: Dispatcher).useServerContext = throwInvalidHookError; -} - const HooksDispatcherOnMount: Dispatcher = { readContext, @@ -2480,10 +2454,6 @@ if (enableCache) { (HooksDispatcherOnMount: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnMount: Dispatcher).useCacheRefresh = mountRefresh; } -if (enableServerContext) { - (HooksDispatcherOnMount: Dispatcher).useServerContext = readContext; -} - const HooksDispatcherOnUpdate: Dispatcher = { readContext, @@ -2511,9 +2481,6 @@ if (enableCache) { (HooksDispatcherOnUpdate: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnUpdate: Dispatcher).useCacheRefresh = updateRefresh; } -if (enableServerContext) { - (HooksDispatcherOnUpdate: Dispatcher).useServerContext = readContext; -} const HooksDispatcherOnRerender: Dispatcher = { readContext, @@ -2542,9 +2509,6 @@ if (enableCache) { (HooksDispatcherOnRerender: Dispatcher).getCacheForType = getCacheForType; (HooksDispatcherOnRerender: Dispatcher).useCacheRefresh = updateRefresh; } -if (enableServerContext) { - (HooksDispatcherOnRerender: Dispatcher).useServerContext = readContext; -} let HooksDispatcherOnMountInDEV: Dispatcher | null = null; let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null; @@ -2720,9 +2684,6 @@ if (__DEV__) { return mountRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnMountInDEV: Dispatcher).useServerContext = mountServerContext; - } HooksDispatcherOnMountWithHookTypesInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -2865,9 +2826,6 @@ if (__DEV__) { return mountRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useServerContext = updateServerContext; - } HooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3010,12 +2968,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = updateServerContext; - } - - if (!enableServerContext) { - } HooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3159,9 +3111,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (HooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = updateServerContext; - } InvalidNestedHooksDispatcherOnMountInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3317,24 +3266,8 @@ if (__DEV__) { (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).getCacheForType = getCacheForType; (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() { currentHookNameInDev = 'useCacheRefresh'; - updateHookTypesDev(); - return mountRefresh(); - }; - } - - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); mountHookTypesDev(); - return readContext(context); + return mountRefresh(); }; } @@ -3496,21 +3429,6 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }; - } InvalidNestedHooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext | ReactServerContext): T { @@ -3671,19 +3589,4 @@ if (__DEV__) { return updateRefresh(); }; } - if (enableServerContext) { - (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useServerContext = < - T: ServerContextJSONValue, - >( - context: ReactServerContext, - ): T => { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - currentHookNameInDev = 'useServerContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }; - } } diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 8a0b2521542..aace3ae0081 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -12,7 +12,6 @@ import type { RefObject, ReactContext, ReactServerContext, - ServerContextJSONValue, MutableSourceSubscribeFn, MutableSourceGetSnapshotFn, MutableSourceVersion, @@ -48,8 +47,7 @@ export type HookType = | 'useMutableSource' | 'useSyncExternalStore' | 'useId' - | 'useCacheRefresh' - | 'useServerContext'; + | 'useCacheRefresh'; export type ContextDependency = { context: ReactContext | ReactServerContext, @@ -384,9 +382,6 @@ export type Dispatcher = {| getSnapshot: MutableSourceGetSnapshotFn, subscribe: MutableSourceSubscribeFn, ): Snapshot, - useServerContext?: ( - context: ReactServerContext, - ) => T, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index 170ac083b60..e595fb2a82f 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -16,7 +16,6 @@ import type { ReactContext, StartTransitionOptions, ReactServerContext, - ServerContextJSONValue, } from 'shared/ReactTypes'; import type {ResponseState} from './ReactServerFormatConfig'; @@ -269,16 +268,6 @@ function useContext(context: ReactContext): T { return readContextImpl(context); } -function useServerContext( - context: ReactServerContext, -): T { - if (__DEV__) { - currentHookNameInDev = 'useServerContext'; - } - resolveCurrentlyRenderingComponent(); - return readContextImpl(context); -} - function basicStateReducer(state: S, action: BasicStateAction): S { // $FlowFixMe: Flow doesn't like mixed types return typeof action === 'function' ? action(state) : action; @@ -556,7 +545,6 @@ function noop(): void {} export const Dispatcher: DispatcherType = { readContext, useContext, - useServerContext, useMemo, useReducer, useRef, diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index 97071a9b289..bd703b357f8 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -69,14 +69,6 @@ export const Dispatcher: DispatcherType = { useEffect: (unsupportedHook: any), useId: (unsupportedHook: any), useMutableSource: (unsupportedHook: any), - useServerContext: function useServerContext( - context: ReactServerContext, - ): T { - if (!currentCache) { - throw new Error('useServerContext is only supported while rendering.'); - } - return readContextImpl(context); - }, useSyncExternalStore: (unsupportedHook: any), useCacheRefresh(): (?() => T, ?T) => void { return unsupportedRefresh; diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index caa3cb381c2..fdb80d1f316 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -72,7 +72,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } @@ -90,7 +90,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } diff --git a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js index 27115bf6b4b..d75f1f7c9b4 100644 --- a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js +++ b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js @@ -46,7 +46,6 @@ export function waitForSuspense(fn: () => T): Promise { useMutableSource: unsupported, useSyncExternalStore: unsupported, useCacheRefresh: unsupported, - useServerContext: unsupported, }; // Not using async/await because we don't compile it. return new Promise((resolve, reject) => { diff --git a/packages/react/index.classic.fb.js b/packages/react/index.classic.fb.js index 122f679fcfd..76326a0fe59 100644 --- a/packages/react/index.classic.fb.js +++ b/packages/react/index.classic.fb.js @@ -56,7 +56,6 @@ export { useMutableSource as unstable_useMutableSource, useReducer, useRef, - useServerContext, useState, useSyncExternalStore, useTransition, diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js index 1ee0c52731f..19af075993d 100644 --- a/packages/react/index.experimental.js +++ b/packages/react/index.experimental.js @@ -49,7 +49,6 @@ export { useMemo, useReducer, useRef, - useServerContext, useState, useSyncExternalStore, useTransition, diff --git a/packages/react/index.js b/packages/react/index.js index aa25252e253..084aabb53c6 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -74,7 +74,6 @@ export { useLayoutEffect, useMemo, useMutableSource, - useServerContext, useSyncExternalStore, useReducer, useRef, diff --git a/packages/react/index.modern.fb.js b/packages/react/index.modern.fb.js index 231f094922f..e9f80ade061 100644 --- a/packages/react/index.modern.fb.js +++ b/packages/react/index.modern.fb.js @@ -55,7 +55,6 @@ export { useMutableSource as unstable_useMutableSource, useReducer, useRef, - useServerContext, useState, useSyncExternalStore, useTransition, diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js index d603d2b62c4..0dedf2d8d90 100644 --- a/packages/react/index.stable.js +++ b/packages/react/index.stable.js @@ -40,7 +40,6 @@ export { useMemo, useReducer, useRef, - useServerContext, useState, useSyncExternalStore, useTransition, diff --git a/packages/react/src/React.js b/packages/react/src/React.js index a64384347a3..d13bf62941b 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -51,7 +51,6 @@ import { useSyncExternalStore, useReducer, useRef, - useServerContext, useState, useTransition, useDeferredValue, @@ -101,7 +100,6 @@ export { useLayoutEffect, useMemo, useMutableSource, - useServerContext, useSyncExternalStore, useReducer, useRef, diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 2769f9e1a32..bfcaf850c69 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -8,8 +8,6 @@ */ import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes'; -import {enableServerContext} from 'shared/ReactFeatureFlags'; -import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; import type { MutableSource, MutableSourceGetSnapshotFn, @@ -190,21 +188,6 @@ export function useMutableSource( return dispatcher.useMutableSource(source, getSnapshot, subscribe); } -export function useServerContext( - Context: ReactServerContext, -): T { - if (enableServerContext) { - if (Context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { - throw new Error( - 'useServerContext expects a context created with React.createServerContext', - ); - } - } - const dispatcher = resolveDispatcher(); - // $FlowFixMe This is unstable, thus optional - return dispatcher.useServerContext(Context); -} - export function useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index caa3cb381c2..fdb80d1f316 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -72,7 +72,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } @@ -90,7 +90,7 @@ function _createServerContext( if (context._definitionLoaded) { return (context: any)._defaultValue; } else { - // If the definition hasn't loaded then we know `useServerContext` is not + // If the definition hasn't loaded then we know `useContext` is not // being called, in this case we'll just return the DEFAULT_PLACEHOLDER // because it won't actually be returned to a component } diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 765824d8931..0049b0a8468 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -404,15 +404,13 @@ "416": "This environment don't support binary chunks.", "417": "React currently only supports piping to one writable stream.", "418": "An error occurred during hydration. The server HTML was replaced with client content", - "419": "useServerContext is only supported while rendering.", - "420": "ServerContext: %s already defined", - "421": "useServerContext expects a context created with React.createServerContext", - "422": "Hydration failed because the initial UI does not match what was rendered on the server.", - "423": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", - "424": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", - "425": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", - "426": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", - "427": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", - "428": "useServerContext expects a context created with React.createServerContext" - + "419": "Hydration failed because the initial UI does not match what was rendered on the server.", + "420": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", + "421": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", + "422": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", + "423": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", + "424": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", + "425": "useServerContext expects a context created with React.createServerContext", + "426": "useServerContext is only supported while rendering.", + "427": "ServerContext: %s already defined" } From 9d30d7be2462427d7f7842b0553e36b381348b1f Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 24 Feb 2022 11:29:24 -0500 Subject: [PATCH 47/83] dont rely on object getters in ReactServerContext and disallow JSX --- .../react-client/src/ReactServerContext.js | 56 ++------------ .../src/__tests__/ReactFlight-test.js | 43 ++++++++--- .../src/__tests__/ReactDOMFizzServer-test.js | 54 ++++++++++++++ .../src/server/ReactPartialRendererHooks.js | 1 + .../src/ReactFiberHooks.new.js | 1 + .../src/ReactFiberHooks.old.js | 1 + .../src/ReactFiberNewContext.new.js | 13 +++- .../src/ReactFiberNewContext.old.js | 13 +++- .../react-server/src/ReactFizzNewContext.js | 19 ++++- packages/react-server/src/ReactFlightHooks.js | 2 +- .../react-server/src/ReactFlightServer.js | 36 ++++++++- .../react-server/src/ReactServerContext.js | 66 +++++------------ packages/react/src/ReactServerContext.js | 74 +++++-------------- .../react/src/ReactServerContextRegistry.js | 4 +- packages/shared/ReactSymbols.js | 3 + packages/shared/ReactTypes.js | 2 - .../shared/forks/ReactFeatureFlags.www.js | 2 +- scripts/error-codes/codes.json | 20 ++--- scripts/jest/matchers/toWarnDev.js | 4 +- 19 files changed, 227 insertions(+), 187 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index fdb80d1f316..0925d1f7c53 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -10,6 +10,7 @@ import { REACT_PROVIDER_TYPE, REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, } from 'shared/ReactSymbols'; import type { @@ -24,11 +25,6 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -let DEFAULT_PLACEHOLDER; -if (enableServerContext) { - DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; -} - export function createServerContext( globalName: string, defaultValue: T, @@ -43,8 +39,7 @@ export function createServerContext( ); } const context = globalServerContextRegistry[globalName]; - if (!context._definitionLoaded) { - context._definitionLoaded = true; + if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { context._defaultValue = defaultValue; } else { throw new Error(`ServerContext: ${globalName} already defined`); @@ -63,47 +58,10 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - __currentValue: defaultValue, - __currentValue2: defaultValue, - - get _currentValue() { - const value = context.__currentValue; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - } - return value; - }, - - set _currentValue(value) { - context.__currentValue = value; - }, - - get _currentValue2() { - const value = context.__currentValue2; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - return (undefined: any); - } - return value; - }, - - set _currentValue2(value) { - context.__currentValue2 = value; - }, + _currentValue: defaultValue, + _currentValue2: defaultValue, - _defaultValue: (undefined: any), + _defaultValue: defaultValue, // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. @@ -132,11 +90,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string, value: any) { +export function getOrCreateServerContext(globalName: string) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - value === undefined ? DEFAULT_PLACEHOLDER : value, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 1dc4f6f8db8..d18e10ba6cf 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -363,6 +363,38 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(
hi this is server
); }); + // @gate enableServerContext + it('errors if you try passing JSX through ServerContext value', () => { + const ServerContext = React.createServerContext('ServerContext', { + foo: { + bar: hi this is default, + }, + }); + + function Foo() { + return ( +
+ hi this is server, + }, + }}> + + +
+ ); + } + function Bar() { + const context = React.useContext(ServerContext); + return context.foo.bar; + } + + expect(() => { + ReactNoopFlightServer.render(); + }).toErrorDev('React elements are not allowed in ServerContext'); + }); + // @gate enableServerContext it('propagates ServerContext and cleansup providers in flight', () => { const ServerContext = React.createServerContext( @@ -595,9 +627,7 @@ describe('ReactFlight', () => { ); } - const transport = ReactNoopFlightServer.render(, { - context: [['ServerContext', 'Override']], - }); + const transport = ReactNoopFlightServer.render(); expect(ClientContext).toBe(undefined); act(() => { @@ -615,12 +645,7 @@ describe('ReactFlight', () => {
test
-
Override
- - {/** In practice this would also be Override because the */} - {/** server context sent up to the server would be around this*/} - {/** tree. For this test we didn't do that though so it uses the */} - {/** real default */} +
default
default
default
diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 90b339bb281..a85830e4080 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -2455,4 +2455,58 @@ describe('ReactDOMFizzServer', () => { 'Suspense boundary. Switched to client rendering.', ]); }); + + // @gate enableServerContext + it('supports ServerContext', async () => { + const {getOrCreateServerContext} = require('react/src/ReactServerContext'); + const ServerContext = getOrCreateServerContext('ServerContext'); + + let initialized = false; + function inlineLazyServerContextInitialization() { + if (!initialized) { + initialized = true; + React.createServerContext('ServerContext', 'default'); + } + } + + function Foo() { + return ( + <> + + + + + + + + + + + + + + + ); + } + function Bar() { + const context = React.useContext(ServerContext); + inlineLazyServerContextInitialization(); + return {context}; + } + + await act(async () => { + ServerContext._currentRenderer = null; + ServerContext._currentRenderer2 = null; + const {pipe} = ReactDOMFizzServer.renderToPipeableStream(); + pipe(writable); + }); + + expect(getVisibleChildren(container)).toEqual([ + hi this is server, + hi this is server2, + hi this is server outer, + hi this is server outer2, + default, + ]); + }); }); diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 1cd95f08fb6..e2280fbd48b 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -14,6 +14,7 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, + ReactServerContext, } from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index ed81d021377..f04e17f74fb 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -15,6 +15,7 @@ import type { StartTransitionOptions, ReactServerContext, ServerContextJSONValue, + ReactServerContext, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.new'; diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 21e93682fe2..7f55803957a 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -15,6 +15,7 @@ import type { StartTransitionOptions, ReactServerContext, ServerContextJSONValue, + ReactServerContext, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.old'; diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 480e86dbfd6..9a516a3fec2 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -49,6 +49,7 @@ import { enableSuspenseServerRenderer, enableLazyContextPropagation, } from 'shared/ReactFeatureFlags'; +import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; const valueCursor: StackCursor = createCursor(null); @@ -136,9 +137,17 @@ export function popProvider( const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = currentValue; + if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + context._currentValue = ((context: any): ReactServerContext)._defaultValue; + } else { + context._currentValue = currentValue; + } } else { - context._currentValue2 = currentValue; + if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + context._currentValue2 = ((context: any): ReactServerContext)._defaultValue; + } else { + context._currentValue2 = currentValue; + } } } diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 78cca5e23b4..2a990c9b040 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -49,6 +49,7 @@ import { enableSuspenseServerRenderer, enableLazyContextPropagation, } from 'shared/ReactFeatureFlags'; +import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; const valueCursor: StackCursor = createCursor(null); @@ -136,9 +137,17 @@ export function popProvider( const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - context._currentValue = currentValue; + if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + context._currentValue = ((context: any): ReactServerContext)._defaultValue; + } else { + context._currentValue = currentValue; + } } else { - context._currentValue2 = currentValue; + if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + context._currentValue2 = ((context: any): ReactServerContext)._defaultValue; + } else { + context._currentValue2 = currentValue; + } } } diff --git a/packages/react-server/src/ReactFizzNewContext.js b/packages/react-server/src/ReactFizzNewContext.js index 01df3d84494..c333b5681de 100644 --- a/packages/react-server/src/ReactFizzNewContext.js +++ b/packages/react-server/src/ReactFizzNewContext.js @@ -7,6 +7,7 @@ * @flow */ +import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; import type {ReactContext, ReactServerContext} from 'shared/ReactTypes'; import {isPrimaryRenderer} from './ReactServerFormatConfig'; @@ -246,7 +247,14 @@ export function popProvider( } } if (isPrimaryRenderer) { - prevSnapshot.context._currentValue = prevSnapshot.parentValue; + const value = prevSnapshot.parentValue; + if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + prevSnapshot.context._currentValue = + // $FlowExpectedError - Effectively refined context to ServerContext + (prevSnapshot.context: ReactServerContext)._defaultValue; + } else { + prevSnapshot.context._currentValue = value; + } if (__DEV__) { if ( context._currentRenderer !== undefined && @@ -261,7 +269,14 @@ export function popProvider( context._currentRenderer = rendererSigil; } } else { - prevSnapshot.context._currentValue2 = prevSnapshot.parentValue; + const value = prevSnapshot.parentValue; + if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + prevSnapshot.context._currentValue2 = + // $FlowExpectedError - Effectively refined context to ServerContext + (prevSnapshot.context: ReactServerContext)._defaultValue; + } else { + prevSnapshot.context._currentValue2 = value; + } if (__DEV__) { if ( context._currentRenderer2 !== undefined && diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index bd703b357f8..0b12e00bfa3 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -59,7 +59,7 @@ export const Dispatcher: DispatcherType = { return entry; }, readContext, - useContext: (unsupportedHook: any), + useContext: (readContext: any), useReducer: (unsupportedHook: any), useRef: (unsupportedHook: any), useState: (unsupportedHook: any), diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index c40e4a891de..1b1b562a675 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -206,6 +206,20 @@ function attemptResolveElement( } case REACT_PROVIDER_TYPE: { pushProvider(type._context, props.value); + if (__DEV__) { + const extraKeys = Object.keys(props).filter(value => { + if (value === 'children' || value === 'value') { + return false; + } + return true; + }); + if (extraKeys.length !== 0) { + throw new Error( + 'ServerContext can only have a value prop and children. Found: ' + + JSON.stringify(extraKeys), + ); + } + } return [ REACT_PROVIDER_TYPE, type._context.displayName, @@ -415,6 +429,9 @@ function isReactElement(value: mixed) { ); } +let insideContextProps = null; +let isInsideContextValue = false; + export function resolveModelToJSON( request: Request, parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray, @@ -448,6 +465,19 @@ export function resolveModelToJSON( ); } + if (__DEV__) { + if (parent[0] === REACT_PROVIDER_TYPE && key === '3') { + insideContextProps = value; + } else if (insideContextProps === parent && key === 'value') { + isInsideContextValue = true; + } else if (insideContextProps === parent && key === 'children') { + isInsideContextValue = false; + } + if (isReactElement(value) && isInsideContextValue) { + throw new Error('React elements are not allowed in ServerContext'); + } + } + // Resolve server components. while (isReactElement(value)) { // TODO: Concatenate keys of parents onto children. @@ -491,6 +521,10 @@ export function resolveModelToJSON( parent[0] === REACT_PROVIDER_TYPE ) { popProvider((value: any)); + if (__DEV__) { + insideContextProps = null; + isInsideContextValue = false; + } return (undefined: any); } @@ -870,7 +904,7 @@ function importServerContexts( if (contexts) { for (let i = 0; i < contexts.length; i++) { const [name, value] = contexts[i]; - const context = getOrCreateServerContext(name, value); + const context = getOrCreateServerContext(name); pushProvider(context, value); registry[name] = context; } diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index fdb80d1f316..790e9485f6d 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -10,6 +10,7 @@ import { REACT_PROVIDER_TYPE, REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, } from 'shared/ReactSymbols'; import type { @@ -24,11 +25,6 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -let DEFAULT_PLACEHOLDER; -if (enableServerContext) { - DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; -} - export function createServerContext( globalName: string, defaultValue: T, @@ -43,9 +39,18 @@ export function createServerContext( ); } const context = globalServerContextRegistry[globalName]; - if (!context._definitionLoaded) { - context._definitionLoaded = true; + if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { context._defaultValue = defaultValue; + if ( + context._currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { + context._currentValue = defaultValue; + } + if ( + context._currentValue2 === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { + context._currentValue2 = defaultValue; + } } else { throw new Error(`ServerContext: ${globalName} already defined`); } @@ -63,47 +68,10 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - __currentValue: defaultValue, - __currentValue2: defaultValue, - - get _currentValue() { - const value = context.__currentValue; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - } - return value; - }, - - set _currentValue(value) { - context.__currentValue = value; - }, - - get _currentValue2() { - const value = context.__currentValue2; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - return (undefined: any); - } - return value; - }, - - set _currentValue2(value) { - context.__currentValue2 = value; - }, + _currentValue: defaultValue, + _currentValue2: defaultValue, - _defaultValue: (undefined: any), + _defaultValue: defaultValue, // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. @@ -132,11 +100,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string, value: any) { +export function getOrCreateServerContext(globalName: string) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - value === undefined ? DEFAULT_PLACEHOLDER : value, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index fdb80d1f316..ce2730e73fa 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -10,6 +10,7 @@ import { REACT_PROVIDER_TYPE, REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, } from 'shared/ReactSymbols'; import type { @@ -24,11 +25,6 @@ import {enableServerContext} from 'shared/ReactFeatureFlags'; const globalServerContextRegistry = ReactSharedInternals.globalServerContextRegistry; -let DEFAULT_PLACEHOLDER; -if (enableServerContext) { - DEFAULT_PLACEHOLDER = globalServerContextRegistry.__defaultValue; -} - export function createServerContext( globalName: string, defaultValue: T, @@ -36,19 +32,22 @@ export function createServerContext( if (!enableServerContext) { throw new Error('Not implemented.'); } - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + let context; + if (globalServerContextRegistry[globalName]) { + context = globalServerContextRegistry[globalName]; + if ( + context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { + context._defaultValue = defaultValue; + } else { + throw new Error(`ServerContext: ${globalName} already defined`); + } + } else { + context = globalServerContextRegistry[globalName] = _createServerContext( globalName, defaultValue, ); } - const context = globalServerContextRegistry[globalName]; - if (!context._definitionLoaded) { - context._definitionLoaded = true; - context._defaultValue = defaultValue; - } else { - throw new Error(`ServerContext: ${globalName} already defined`); - } return context; } @@ -63,47 +62,10 @@ function _createServerContext( // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. - __currentValue: defaultValue, - __currentValue2: defaultValue, - - get _currentValue() { - const value = context.__currentValue; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - } - return value; - }, - - set _currentValue(value) { - context.__currentValue = value; - }, - - get _currentValue2() { - const value = context.__currentValue2; - if (value === DEFAULT_PLACEHOLDER) { - if (context._definitionLoaded) { - return (context: any)._defaultValue; - } else { - // If the definition hasn't loaded then we know `useContext` is not - // being called, in this case we'll just return the DEFAULT_PLACEHOLDER - // because it won't actually be returned to a component - } - return (undefined: any); - } - return value; - }, - - set _currentValue2(value) { - context.__currentValue2 = value; - }, + _currentValue: defaultValue, + _currentValue2: defaultValue, - _defaultValue: (undefined: any), + _defaultValue: defaultValue, // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. @@ -132,11 +94,11 @@ function _createServerContext( // definition is loaded on the server. We'll create it with a null default value // if thats the case and when the definition loads it will set the correct // default value. -export function getOrCreateServerContext(globalName: string, value: any) { +export function getOrCreateServerContext(globalName: string) { if (!globalServerContextRegistry[globalName]) { globalServerContextRegistry[globalName] = _createServerContext( globalName, - value === undefined ? DEFAULT_PLACEHOLDER : value, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } return globalServerContextRegistry[globalName]; diff --git a/packages/react/src/ReactServerContextRegistry.js b/packages/react/src/ReactServerContextRegistry.js index f24d9474309..c98f12b6e35 100644 --- a/packages/react/src/ReactServerContextRegistry.js +++ b/packages/react/src/ReactServerContextRegistry.js @@ -2,6 +2,4 @@ import type {ReactServerContext} from 'shared/ReactTypes'; export const globalServerContextRegistry: { [globalName: string]: ReactServerContext, -} = { - __defaultValue: {}, -}; +} = {}; diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index 01924071581..a9147c9d1d1 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -33,6 +33,9 @@ export const REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen'); export const REACT_LEGACY_HIDDEN_TYPE = Symbol.for('react.legacy_hidden'); export const REACT_CACHE_TYPE = Symbol.for('react.cache'); export const REACT_TRACING_MARKER_TYPE = Symbol.for('react.tracing_marker'); +export const REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = Symbol.for( + 'react.server_context.defaultValue', +); const MAYBE_ITERATOR_SYMBOL = Symbol.iterator; const FAUX_ITERATOR_SYMBOL = '@@iterator'; diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 6625b5bf850..5432815232d 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -88,8 +88,6 @@ export type ReactServerContext = { $$typeof: Symbol | number, Provider: ReactServerProviderType, _defaultValue: T, - __currentValue: T, - __currentValue2: T, _currentValue: T, _currentValue2: T, _currentRenderer?: Object | null, diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index a978d016310..9c73333bfd1 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -101,7 +101,7 @@ export const deletedTreeCleanUpLevel = 3; export const enablePersistentOffscreenHostContainer = false; export const consoleManagedByDevToolsDuringStrictMode = true; -export const enableServerContext = false; +export const enableServerContext = true; // Some www surfaces are still using this. Remove once they have been migrated. export const enableUseMutableSource = true; diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 0049b0a8468..eb351b2dd04 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -404,13 +404,15 @@ "416": "This environment don't support binary chunks.", "417": "React currently only supports piping to one writable stream.", "418": "An error occurred during hydration. The server HTML was replaced with client content", - "419": "Hydration failed because the initial UI does not match what was rendered on the server.", - "420": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", - "421": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", - "422": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", - "423": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", - "424": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", - "425": "useServerContext expects a context created with React.createServerContext", - "426": "useServerContext is only supported while rendering.", - "427": "ServerContext: %s already defined" + "420": "Hydration failed because the initial UI does not match what was rendered on the server.", + "421": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", + "422": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", + "423": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", + "424": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", + "425": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", + "426": "ServerContext can only have a value prop and children. Found: %s", + "427": "React elements are not allowed in ServerContext", + "428": "useServerContext expects a context created with React.createServerContext", + "429": "useServerContext is only supported while rendering.", + "430": "ServerContext: %s already defined" } diff --git a/scripts/jest/matchers/toWarnDev.js b/scripts/jest/matchers/toWarnDev.js index 5e2a144b4e2..dd395d30d11 100644 --- a/scripts/jest/matchers/toWarnDev.js +++ b/scripts/jest/matchers/toWarnDev.js @@ -86,7 +86,9 @@ const createMatcherFor = (consoleMethod, matcherName) => // doesn't match the number of arguments. // We'll fail the test if it happens. let argIndex = 0; - format.replace(/%s/g, () => argIndex++); + if (format.replace) { + format.replace(/%s/g, () => argIndex++); + } if (argIndex !== args.length) { lastWarningWithMismatchingFormat = { format, From 50594a260d2f378b64278b27a8857117ebf88cf9 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 24 Feb 2022 11:37:00 -0500 Subject: [PATCH 48/83] add symbols to devtools + rename globalServerContextRegistry to just ContextRegistry --- packages/react-client/src/ReactServerContext.js | 17 ++++++++--------- .../src/__tests__/ReactFlight-test.js | 7 +++---- .../src/backend/ReactSymbols.js | 7 +++++++ .../src/backend/renderer.js | 4 ++++ packages/react-server/src/ReactServerContext.js | 17 ++++++++--------- packages/react/src/ReactServerContext.js | 17 ++++++++--------- .../react/src/ReactServerContextRegistry.js | 2 +- packages/react/src/ReactSharedInternals.js | 4 ++-- .../react/src/forks/ReactSharedInternals.umd.js | 4 ++-- 9 files changed, 43 insertions(+), 36 deletions(-) diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js index 0925d1f7c53..61af8e3ec31 100644 --- a/packages/react-client/src/ReactServerContext.js +++ b/packages/react-client/src/ReactServerContext.js @@ -22,8 +22,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals'; import {enableServerContext} from 'shared/ReactFeatureFlags'; -const globalServerContextRegistry = - ReactSharedInternals.globalServerContextRegistry; +const ContextRegistry = ReactSharedInternals.ContextRegistry; export function createServerContext( globalName: string, @@ -32,13 +31,13 @@ export function createServerContext( if (!enableServerContext) { throw new Error('Not implemented.'); } - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = _createServerContext( globalName, defaultValue, ); } - const context = globalServerContextRegistry[globalName]; + const context = ContextRegistry[globalName]; if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { context._defaultValue = defaultValue; } else { @@ -81,7 +80,7 @@ function _createServerContext( context._currentRenderer = null; context._currentRenderer2 = null; } - globalServerContextRegistry[globalName] = context; + ContextRegistry[globalName] = context; return context; } @@ -91,11 +90,11 @@ function _createServerContext( // if thats the case and when the definition loads it will set the correct // default value. export function getOrCreateServerContext(globalName: string) { - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = _createServerContext( globalName, REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } - return globalServerContextRegistry[globalName]; + return ContextRegistry[globalName]; } diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index d18e10ba6cf..b4d1eaa1621 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -18,7 +18,7 @@ let ReactNoopFlightClient; let ErrorBoundary; let NoErrorExpected; let Scheduler; -let globalServerContextRegistry; +let ContextRegistry; describe('ReactFlight', () => { beforeEach(() => { @@ -32,8 +32,7 @@ describe('ReactFlight', () => { Scheduler = require('scheduler'); const ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - globalServerContextRegistry = - ReactSharedInternals.globalServerContextRegistry; + ContextRegistry = ReactSharedInternals.ContextRegistry; ErrorBoundary = class extends React.Component { state = {hasError: false, error: null}; @@ -631,7 +630,7 @@ describe('ReactFlight', () => { expect(ClientContext).toBe(undefined); act(() => { - delete globalServerContextRegistry.ServerContext; + delete ContextRegistry.ServerContext; ServerContext._currentRenderer = null; ServerContext._currentRenderer2 = null; const serverModel = ReactNoopFlightClient.read(transport); diff --git a/packages/react-devtools-shared/src/backend/ReactSymbols.js b/packages/react-devtools-shared/src/backend/ReactSymbols.js index ebc6920be8d..cbeb1b9c918 100644 --- a/packages/react-devtools-shared/src/backend/ReactSymbols.js +++ b/packages/react-devtools-shared/src/backend/ReactSymbols.js @@ -19,6 +19,9 @@ export const CONCURRENT_MODE_SYMBOL_STRING = 'Symbol(react.concurrent_mode)'; export const CONTEXT_NUMBER = 0xeace; export const CONTEXT_SYMBOL_STRING = 'Symbol(react.context)'; +export const SERVER_CONTEXT_NUMBER = 0xeacf; +export const SERVER_CONTEXT_SYMBOL_STRING = 'Symbol(react.server_context)'; + export const DEPRECATED_ASYNC_MODE_SYMBOL_STRING = 'Symbol(react.async_mode)'; export const ELEMENT_NUMBER = 0xeac7; @@ -60,3 +63,7 @@ export const SUSPENSE_SYMBOL_STRING = 'Symbol(react.suspense)'; export const SUSPENSE_LIST_NUMBER = 0xead8; export const SUSPENSE_LIST_SYMBOL_STRING = 'Symbol(react.suspense_list)'; + +export const SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED_NUMBER = 0xeae6; +export const SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED_SYMBOL_STRING = + 'Symbol(react.server_context.defaultValue)'; diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js index 88c1d536083..70225e8281c 100644 --- a/packages/react-devtools-shared/src/backend/renderer.js +++ b/packages/react-devtools-shared/src/backend/renderer.js @@ -85,6 +85,8 @@ import { FORWARD_REF_SYMBOL_STRING, MEMO_NUMBER, MEMO_SYMBOL_STRING, + SERVER_CONTEXT_NUMBER, + SERVER_CONTEXT_SYMBOL_STRING, } from './ReactSymbols'; import {format} from './utils'; import { @@ -511,6 +513,8 @@ export function getInternalReactConstants( return `${resolvedContext.displayName || 'Context'}.Provider`; case CONTEXT_NUMBER: case CONTEXT_SYMBOL_STRING: + case SERVER_CONTEXT_NUMBER: + case SERVER_CONTEXT_SYMBOL_STRING: // 16.3-16.5 read from "type" because the Consumer is the actual context object. // 16.6+ should read from "type._context" because Consumer can be different (in DEV). // NOTE Keep in sync with inspectElementRaw() diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js index 790e9485f6d..eacfbdf0640 100644 --- a/packages/react-server/src/ReactServerContext.js +++ b/packages/react-server/src/ReactServerContext.js @@ -22,8 +22,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals'; import {enableServerContext} from 'shared/ReactFeatureFlags'; -const globalServerContextRegistry = - ReactSharedInternals.globalServerContextRegistry; +const ContextRegistry = ReactSharedInternals.ContextRegistry; export function createServerContext( globalName: string, @@ -32,13 +31,13 @@ export function createServerContext( if (!enableServerContext) { throw new Error('Not implemented.'); } - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = _createServerContext( globalName, defaultValue, ); } - const context = globalServerContextRegistry[globalName]; + const context = ContextRegistry[globalName]; if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { context._defaultValue = defaultValue; if ( @@ -91,7 +90,7 @@ function _createServerContext( context._currentRenderer = null; context._currentRenderer2 = null; } - globalServerContextRegistry[globalName] = context; + ContextRegistry[globalName] = context; return context; } @@ -101,11 +100,11 @@ function _createServerContext( // if thats the case and when the definition loads it will set the correct // default value. export function getOrCreateServerContext(globalName: string) { - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = _createServerContext( globalName, REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } - return globalServerContextRegistry[globalName]; + return ContextRegistry[globalName]; } diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index ce2730e73fa..a236de4b2a4 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -22,8 +22,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals'; import {enableServerContext} from 'shared/ReactFeatureFlags'; -const globalServerContextRegistry = - ReactSharedInternals.globalServerContextRegistry; +const ContextRegistry = ReactSharedInternals.ContextRegistry; export function createServerContext( globalName: string, @@ -33,8 +32,8 @@ export function createServerContext( throw new Error('Not implemented.'); } let context; - if (globalServerContextRegistry[globalName]) { - context = globalServerContextRegistry[globalName]; + if (ContextRegistry[globalName]) { + context = ContextRegistry[globalName]; if ( context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED ) { @@ -43,7 +42,7 @@ export function createServerContext( throw new Error(`ServerContext: ${globalName} already defined`); } } else { - context = globalServerContextRegistry[globalName] = _createServerContext( + context = ContextRegistry[globalName] = _createServerContext( globalName, defaultValue, ); @@ -85,7 +84,7 @@ function _createServerContext( context._currentRenderer = null; context._currentRenderer2 = null; } - globalServerContextRegistry[globalName] = context; + ContextRegistry[globalName] = context; return context; } @@ -95,11 +94,11 @@ function _createServerContext( // if thats the case and when the definition loads it will set the correct // default value. export function getOrCreateServerContext(globalName: string) { - if (!globalServerContextRegistry[globalName]) { - globalServerContextRegistry[globalName] = _createServerContext( + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = _createServerContext( globalName, REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, ); } - return globalServerContextRegistry[globalName]; + return ContextRegistry[globalName]; } diff --git a/packages/react/src/ReactServerContextRegistry.js b/packages/react/src/ReactServerContextRegistry.js index c98f12b6e35..dda738ac743 100644 --- a/packages/react/src/ReactServerContextRegistry.js +++ b/packages/react/src/ReactServerContextRegistry.js @@ -1,5 +1,5 @@ import type {ReactServerContext} from 'shared/ReactTypes'; -export const globalServerContextRegistry: { +export const ContextRegistry: { [globalName: string]: ReactServerContext, } = {}; diff --git a/packages/react/src/ReactSharedInternals.js b/packages/react/src/ReactSharedInternals.js index 1f7debf27b7..6f160b96ec8 100644 --- a/packages/react/src/ReactSharedInternals.js +++ b/packages/react/src/ReactSharedInternals.js @@ -11,7 +11,7 @@ import ReactCurrentActQueue from './ReactCurrentActQueue'; import ReactCurrentOwner from './ReactCurrentOwner'; import ReactDebugCurrentFrame from './ReactDebugCurrentFrame'; import {enableServerContext} from 'shared/ReactFeatureFlags'; -import {globalServerContextRegistry} from './ReactServerContextRegistry'; +import {ContextRegistry} from './ReactServerContextRegistry'; const ReactSharedInternals = { ReactCurrentDispatcher, @@ -25,7 +25,7 @@ if (__DEV__) { } if (enableServerContext) { - ReactSharedInternals.globalServerContextRegistry = globalServerContextRegistry; + ReactSharedInternals.ContextRegistry = ContextRegistry; } export default ReactSharedInternals; diff --git a/packages/react/src/forks/ReactSharedInternals.umd.js b/packages/react/src/forks/ReactSharedInternals.umd.js index a41c572462b..57e96e66547 100644 --- a/packages/react/src/forks/ReactSharedInternals.umd.js +++ b/packages/react/src/forks/ReactSharedInternals.umd.js @@ -12,7 +12,7 @@ import ReactCurrentOwner from '../ReactCurrentOwner'; import ReactDebugCurrentFrame from '../ReactDebugCurrentFrame'; import ReactCurrentBatchConfig from '../ReactCurrentBatchConfig'; import {enableServerContext} from 'shared/ReactFeatureFlags'; -import {globalServerContextRegistry} from '../ReactServerContextRegistry'; +import {ContextRegistry} from '../ReactServerContextRegistry'; const ReactSharedInternals = { ReactCurrentDispatcher, @@ -33,7 +33,7 @@ if (__DEV__) { } if (enableServerContext) { - ReactSharedInternals.globalServerContextRegistry = globalServerContextRegistry; + ReactSharedInternals.ContextRegistry = ContextRegistry; } export default ReactSharedInternals; From 43a71b2bce097b446c026cfb1cfafae90a7f2f61 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 24 Feb 2022 11:50:57 -0500 Subject: [PATCH 49/83] gate test case as experimental --- packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index a85830e4080..9b741b38ccd 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -2456,7 +2456,7 @@ describe('ReactDOMFizzServer', () => { ]); }); - // @gate enableServerContext + // @gate enableServerContext && experimental it('supports ServerContext', async () => { const {getOrCreateServerContext} = require('react/src/ReactServerContext'); const ServerContext = getOrCreateServerContext('ServerContext'); From 69a9ccd9c97db6a792762602f3df1b004b59adf4 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 13:34:53 -0500 Subject: [PATCH 50/83] feedback --- .../react-client/src/ReactFlightClient.js | 9 +- .../react-client/src/ReactServerContext.js | 100 ---------------- .../src/__tests__/ReactFlight-test.js | 10 +- .../react-debug-tools/src/ReactDebugHooks.js | 16 +-- .../src/__tests__/ReactDOMFizzServer-test.js | 18 ++- .../src/server/ReactPartialRenderer.js | 11 +- .../src/server/ReactPartialRendererContext.js | 2 +- .../src/ReactNoopFlightServer.js | 8 +- .../src/ReactFiberBeginWork.new.js | 3 +- .../src/ReactFiberBeginWork.old.js | 3 +- .../src/ReactFiberNewContext.new.js | 5 +- .../src/ReactFiberNewContext.old.js | 5 +- .../src/ReactFiberWorkLoop.new.js | 1 + .../src/getComponentNameFromFiber.js | 2 +- .../src/ReactFlightDOMServerBrowser.js | 15 ++- .../src/ReactFlightDOMServerNode.js | 15 ++- .../react-server/src/ReactFlightServer.js | 33 +++--- .../react-server/src/ReactServerContext.js | 110 ------------------ packages/react/src/React.js | 2 +- packages/react/src/ReactServerContext.js | 104 ----------------- packages/shared/ReactServerContext.js | 92 +++++++++++++++ packages/shared/ReactServerContextRegistry.js | 24 ++++ packages/shared/ReactTypes.js | 17 +-- packages/shared/getComponentNameFromType.js | 2 +- scripts/jest/matchers/toWarnDev.js | 4 +- 25 files changed, 198 insertions(+), 413 deletions(-) delete mode 100644 packages/react-client/src/ReactServerContext.js delete mode 100644 packages/react-server/src/ReactServerContext.js delete mode 100644 packages/react/src/ReactServerContext.js create mode 100644 packages/shared/ReactServerContext.js create mode 100644 packages/shared/ReactServerContextRegistry.js diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 90a960e3a61..365565067f5 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -24,14 +24,14 @@ import { parseModel, } from './ReactFlightClientHostConfig'; -import {getOrCreateServerContext} from './ReactServerContext'; - import { REACT_LAZY_TYPE, REACT_ELEMENT_TYPE, REACT_PROVIDER_TYPE, } from 'shared/ReactSymbols'; +import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry'; + export type JSONValue = | number | null @@ -324,11 +324,6 @@ export function parseModelString( // When passed into React, we'll know how to suspend on this. return createLazyChunkWrapper(chunk); } - case '!': { - if (value === '!') { - return REACT_PROVIDER_TYPE; - } - } } return value; } diff --git a/packages/react-client/src/ReactServerContext.js b/packages/react-client/src/ReactServerContext.js deleted file mode 100644 index 61af8e3ec31..00000000000 --- a/packages/react-client/src/ReactServerContext.js +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import { - REACT_PROVIDER_TYPE, - REACT_SERVER_CONTEXT_TYPE, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, -} from 'shared/ReactSymbols'; - -import type { - ReactServerContext, - ServerContextJSONValue, -} from 'shared/ReactTypes'; - -import ReactSharedInternals from 'shared/ReactSharedInternals'; - -import {enableServerContext} from 'shared/ReactFeatureFlags'; - -const ContextRegistry = ReactSharedInternals.ContextRegistry; - -export function createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = _createServerContext( - globalName, - defaultValue, - ); - } - const context = ContextRegistry[globalName]; - if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - context._defaultValue = defaultValue; - } else { - throw new Error(`ServerContext: ${globalName} already defined`); - } - return context; -} - -function _createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - const context: ReactServerContext = { - $$typeof: REACT_SERVER_CONTEXT_TYPE, - // As a workaround to support multiple concurrent renderers, we categorize - // some renderers as primary and others as secondary. We only expect - // there to be two concurrent renderers at most: React Native (primary) and - // Fabric (secondary); React DOM (primary) and React ART (secondary). - // Secondary renderers store their context values on separate fields. - _currentValue: defaultValue, - _currentValue2: defaultValue, - - _defaultValue: defaultValue, - - // Used to track how many concurrent renderers this context currently - // supports within in a single renderer. Such as parallel server rendering. - _threadCount: 0, - _definitionLoaded: false, - // These are circular - Provider: (null: any), - displayName: globalName, - }; - - context.Provider = { - $$typeof: REACT_PROVIDER_TYPE, - _context: context, - }; - - if (__DEV__) { - context._currentRenderer = null; - context._currentRenderer2 = null; - } - ContextRegistry[globalName] = context; - return context; -} - -// This function is called by FlightClient to create a server context sent from -// the server. Its possible that FlightClient is creating it before the -// definition is loaded on the server. We'll create it with a null default value -// if thats the case and when the definition loads it will set the correct -// default value. -export function getOrCreateServerContext(globalName: string) { - if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = _createServerContext( - globalName, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, - ); - } - return ContextRegistry[globalName]; -} diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index b4d1eaa1621..9197a4c3bfe 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -391,7 +391,9 @@ describe('ReactFlight', () => { expect(() => { ReactNoopFlightServer.render(); - }).toErrorDev('React elements are not allowed in ServerContext'); + }).toErrorDev('React elements are not allowed in ServerContext', { + withoutStack: true, + }); }); // @gate enableServerContext @@ -556,9 +558,9 @@ describe('ReactFlight', () => { function Bar() { return {React.useContext(ServerContext)}; } - const transport = ReactNoopFlightServer.render(, { - context: [['ServerContext', 'Override']], - }); + const transport = ReactNoopFlightServer.render(, {}, [ + ['ServerContext', 'Override'], + ]); act(() => { const flightModel = ReactNoopFlightClient.read(transport); diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index a651dde86e5..c9f463471bf 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -107,9 +107,7 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext( - context: ReactContext | ReactServerContext, -): T { +function readContext(context: ReactContext): T { // For now we don't expose readContext usage in the hooks debugging info. return context._currentValue; } @@ -682,16 +680,12 @@ export function inspectHooks( return buildTree(rootStack, readHookLog, includeHooksSource); } -function setupContexts( - contextMap: Map | ReactServerContext, any>, - fiber: Fiber, -) { +function setupContexts(contextMap: Map, any>, fiber: Fiber) { let current = fiber; while (current) { if (current.tag === ContextProvider) { const providerType: ReactProviderType = current.type; - const context: ReactContext | ReactServerContext = - providerType._context; + const context: ReactContext = providerType._context; if (!contextMap.has(context)) { // Store the current value that we're going to restore later. contextMap.set(context, context._currentValue); @@ -703,9 +697,7 @@ function setupContexts( } } -function restoreContexts( - contextMap: Map | ReactServerContext, any>, -) { +function restoreContexts(contextMap: Map, any>) { contextMap.forEach((value, context) => (context._currentValue = value)); } diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 9b741b38ccd..ce6121ca926 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -2458,18 +2458,17 @@ describe('ReactDOMFizzServer', () => { // @gate enableServerContext && experimental it('supports ServerContext', async () => { - const {getOrCreateServerContext} = require('react/src/ReactServerContext'); - const ServerContext = getOrCreateServerContext('ServerContext'); - - let initialized = false; + let ServerContext; function inlineLazyServerContextInitialization() { - if (!initialized) { - initialized = true; - React.createServerContext('ServerContext', 'default'); + if (!ServerContext) { + console.log({ServerContext}); + ServerContext = React.createServerContext('ServerContext', 'default'); } + return ServerContext; } function Foo() { + inlineLazyServerContextInitialization(); return ( <> @@ -2489,14 +2488,11 @@ describe('ReactDOMFizzServer', () => { ); } function Bar() { - const context = React.useContext(ServerContext); - inlineLazyServerContextInitialization(); + const context = React.useContext(inlineLazyServerContextInitialization()); return {context}; } await act(async () => { - ServerContext._currentRenderer = null; - ServerContext._currentRenderer2 = null; const {pipe} = ReactDOMFizzServer.renderToPipeableStream(); pipe(writable); }); diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 9215a9b49ae..f7156a50535 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -781,7 +781,7 @@ class ReactDOMServerRenderer { suspenseDepth: number; contextIndex: number; - contextStack: Array | ReactServerContext>; + contextStack: Array>; contextValueStack: Array; contextProviderStack: ?Array>; // DEV-only @@ -839,8 +839,7 @@ class ReactDOMServerRenderer { pushProvider(provider: ReactProvider): void { const index = ++this.contextIndex; - const context: ReactContext | ReactServerContext = - provider.type._context; + const context: ReactContext = provider.type._context; const threadID = this.threadID; validateContextBounds(context, threadID); const previousValue = context[threadID]; @@ -865,8 +864,7 @@ class ReactDOMServerRenderer { } } - const context: ReactContext | ReactServerContext = this - .contextStack[index]; + const context: ReactContext = this.contextStack[index]; const previousValue = this.contextValueStack[index]; // "Hide" these null assignments from Flow by using `any` @@ -888,8 +886,7 @@ class ReactDOMServerRenderer { clearProviders(): void { // Restore any remaining providers on the stack to previous values for (let index = this.contextIndex; index >= 0; index--) { - const context: ReactContext | ReactServerContext = this - .contextStack[index]; + const context: ReactContext = this.contextStack[index]; const previousValue = this.contextValueStack[index]; context[this.threadID] = previousValue; } diff --git a/packages/react-dom/src/server/ReactPartialRendererContext.js b/packages/react-dom/src/server/ReactPartialRendererContext.js index bdcd9f4ba65..f51dcb27b14 100644 --- a/packages/react-dom/src/server/ReactPartialRendererContext.js +++ b/packages/react-dom/src/server/ReactPartialRendererContext.js @@ -44,7 +44,7 @@ function checkContextTypes(typeSpecs, values, location: string) { } export function validateContextBounds( - context: ReactContext | ReactServerContext, + context: ReactContext, threadID: ThreadID, ) { // If we don't have enough slots in this context to store this threadID, diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index 15e991fa715..8a1ad32a2a9 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -68,16 +68,20 @@ type ServerContextJSONValue = type Options = { onError?: (error: mixed) => void, - context?: Array<[string, ServerContextJSONValue]>, }; -function render(model: ReactModel, options?: Options): Destination { +function render( + model: ReactModel, + options?: Options, + context?: Array<[string, ServerContextJSONValue]>, +): Destination { const destination: Destination = []; const bundlerConfig = undefined; const request = ReactNoopFlightServer.createRequest( model, bundlerConfig, options, + context, ); ReactNoopFlightServer.startWork(request); ReactNoopFlightServer.startFlowing(request, destination); diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index fef0efef078..ee97b8d4838 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -3221,8 +3221,7 @@ function updateContextProvider( renderLanes: Lanes, ) { const providerType: ReactProviderType = workInProgress.type; - const context: ReactContext | ReactServerContext = - providerType._context; + const context: ReactContext = providerType._context; const newProps = workInProgress.pendingProps; const oldProps = workInProgress.memoizedProps; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index ab0b17caafb..9de275b9cfa 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -3221,8 +3221,7 @@ function updateContextProvider( renderLanes: Lanes, ) { const providerType: ReactProviderType = workInProgress.type; - const context: ReactContext | ReactServerContext = - providerType._context; + const context: ReactContext = providerType._context; const newProps = workInProgress.pendingProps; const oldProps = workInProgress.memoizedProps; diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 9a516a3fec2..13b75a56391 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -131,7 +131,7 @@ export function pushProvider( } export function popProvider( - context: ReactContext | ReactServerContext, + context: ReactContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; @@ -550,8 +550,7 @@ function propagateParentContextChanges( const oldProps = currentParent.memoizedProps; if (oldProps !== null) { const providerType: ReactProviderType = parent.type; - const context: ReactContext | ReactServerContext = - providerType._context; + const context: ReactContext = providerType._context; const newProps = parent.pendingProps; const newValue = newProps.value; diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 2a990c9b040..0909254b6f5 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -131,7 +131,7 @@ export function pushProvider( } export function popProvider( - context: ReactContext | ReactServerContext, + context: ReactContext, providerFiber: Fiber, ): void { const currentValue = valueCursor.current; @@ -550,8 +550,7 @@ function propagateParentContextChanges( const oldProps = currentParent.memoizedProps; if (oldProps !== null) { const providerType: ReactProviderType = parent.type; - const context: ReactContext | ReactServerContext = - providerType._context; + const context: ReactContext = providerType._context; const newProps = parent.pendingProps; const newValue = newProps.value; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 1516817638c..7223ad7d052 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -1555,6 +1555,7 @@ function handleError(root, thrownValue): void { ); } } + throwException( root, erroredWork.return, diff --git a/packages/react-reconciler/src/getComponentNameFromFiber.js b/packages/react-reconciler/src/getComponentNameFromFiber.js index a05595624b4..dec6c3d5caf 100644 --- a/packages/react-reconciler/src/getComponentNameFromFiber.js +++ b/packages/react-reconciler/src/getComponentNameFromFiber.js @@ -57,7 +57,7 @@ function getWrappedName( } // Keep in sync with shared/getComponentNameFromType -function getContextName(type: ReactContext | ReactServerContext) { +function getContextName(type: ReactContext) { return type.displayName || 'Context'; } diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js index 4b6a8b2856c..a9cc8e50914 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js @@ -7,10 +7,8 @@ * @flow */ -import type { - ReactModel, - RequestOptions, -} from 'react-server/src/ReactFlightServer'; +import type {ReactModel} from 'react-server/src/ReactFlightServer'; +import type {ServerContextJSONValue} from 'shared/ReactTypes'; import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig'; import { @@ -19,12 +17,17 @@ import { startFlowing, } from 'react-server/src/ReactFlightServer'; +type Options = { + onError?: (error: mixed) => void, +}; + function renderToReadableStream( model: ReactModel, webpackMap: BundlerConfig, - options?: RequestOptions, + options?: Options, + context?: Array<[string, ServerContextJSONValue]>, ): ReadableStream { - const request = createRequest(model, webpackMap, options); + const request = createRequest(model, webpackMap, options, context); const stream = new ReadableStream({ type: 'bytes', start(controller) { diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js index 92abb8afae1..bb990c9e179 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js @@ -7,12 +7,10 @@ * @flow */ -import type { - ReactModel, - RequestOptions, -} from 'react-server/src/ReactFlightServer'; +import type {ReactModel} from 'react-server/src/ReactFlightServer'; import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig'; import type {Writable} from 'stream'; +import type {ServerContextJSONValue} from 'shared/ReactTypes'; import { createRequest, @@ -24,6 +22,10 @@ function createDrainHandler(destination, request) { return () => startFlowing(request, destination); } +type Options = { + onError?: (error: mixed) => void, +}; + type Controls = {| pipe(destination: T): T, |}; @@ -31,9 +33,10 @@ type Controls = {| function renderToPipeableStream( model: ReactModel, webpackMap: BundlerConfig, - options?: RequestOptions, + options?: Options, + context?: Array<[string, ServerContextJSONValue]>, ): Controls { - const request = createRequest(model, webpackMap, options); + const request = createRequest(model, webpackMap, options, context); let hasStartedFlowing = false; startWork(request); return { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 1b1b562a675..749c91b2485 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -38,7 +38,6 @@ import { isModuleReference, } from './ReactFlightServerConfig'; -import {getOrCreateServerContext} from './ReactServerContext'; import {Dispatcher, getCurrentCache, setCurrentCache} from './ReactFlightHooks'; import { pushProvider, @@ -56,11 +55,15 @@ import { REACT_MEMO_TYPE, REACT_PROVIDER_TYPE, REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, } from 'shared/ReactSymbols'; +import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import isArray from 'shared/isArray'; +import {createServerContext} from 'react'; + type ReactJSONValue = | string | boolean @@ -105,12 +108,12 @@ export type Request = { toJSON: (key: string, value: ReactModel) => ReactJSONValue, }; -export type RequestOptions = { +export type Options = { onError?: (error: mixed) => void, - context?: Array<[string, ServerContextJSONValue]>, }; const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; +const ContextRegistry = ReactSharedInternals.ContextRegistry; function defaultErrorHandler(error: mixed) { console['error'](error); @@ -124,7 +127,8 @@ const CLOSED = 2; export function createRequest( model: ReactModel, bundlerConfig: BundlerConfig, - options?: RequestOptions, + options?: Options, + context?: Array<[string, ServerContextJSONValue]>, ): Request { const pingedSegments = []; const onError = options ? options.onError : undefined; @@ -148,8 +152,8 @@ export function createRequest( }, }; request.pendingChunks++; - const context = createRootContext(options ? options.context : undefined); - const rootSegment = createSegment(request, model, context); + const rootContext = createRootContext(context); + const rootSegment = createSegment(request, model, rootContext); pingedSegments.push(rootSegment); return request; } @@ -224,8 +228,8 @@ function attemptResolveElement( REACT_PROVIDER_TYPE, type._context.displayName, key, - props, - type._context, + // Rely on __popProvider being serialized last to pop the provider. + {...props, __popProvider$$: type._context}, ]; } } @@ -457,8 +461,6 @@ export function resolveModelToJSON( switch (value) { case REACT_ELEMENT_TYPE: return '$'; - case REACT_PROVIDER_TYPE: - return '!'; case REACT_LAZY_TYPE: throw new Error( 'React Lazy Components are not yet supported on the server.', @@ -473,13 +475,15 @@ export function resolveModelToJSON( } else if (insideContextProps === parent && key === 'children') { isInsideContextValue = false; } - if (isReactElement(value) && isInsideContextValue) { - throw new Error('React elements are not allowed in ServerContext'); - } } // Resolve server components. while (isReactElement(value)) { + if (__DEV__) { + if (isInsideContextValue) { + console.error('React elements are not allowed in ServerContext'); + } + } // TODO: Concatenate keys of parents onto children. const element: React$Element = (value: any); try { @@ -517,8 +521,7 @@ export function resolveModelToJSON( if ( value.$$typeof === REACT_SERVER_CONTEXT_TYPE && - key === '4' && - parent[0] === REACT_PROVIDER_TYPE + key === '__popProvider$$' ) { popProvider((value: any)); if (__DEV__) { diff --git a/packages/react-server/src/ReactServerContext.js b/packages/react-server/src/ReactServerContext.js deleted file mode 100644 index eacfbdf0640..00000000000 --- a/packages/react-server/src/ReactServerContext.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import { - REACT_PROVIDER_TYPE, - REACT_SERVER_CONTEXT_TYPE, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, -} from 'shared/ReactSymbols'; - -import type { - ReactServerContext, - ServerContextJSONValue, -} from 'shared/ReactTypes'; - -import ReactSharedInternals from 'shared/ReactSharedInternals'; - -import {enableServerContext} from 'shared/ReactFeatureFlags'; - -const ContextRegistry = ReactSharedInternals.ContextRegistry; - -export function createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = _createServerContext( - globalName, - defaultValue, - ); - } - const context = ContextRegistry[globalName]; - if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - context._defaultValue = defaultValue; - if ( - context._currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED - ) { - context._currentValue = defaultValue; - } - if ( - context._currentValue2 === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED - ) { - context._currentValue2 = defaultValue; - } - } else { - throw new Error(`ServerContext: ${globalName} already defined`); - } - return context; -} - -function _createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - const context: ReactServerContext = { - $$typeof: REACT_SERVER_CONTEXT_TYPE, - // As a workaround to support multiple concurrent renderers, we categorize - // some renderers as primary and others as secondary. We only expect - // there to be two concurrent renderers at most: React Native (primary) and - // Fabric (secondary); React DOM (primary) and React ART (secondary). - // Secondary renderers store their context values on separate fields. - _currentValue: defaultValue, - _currentValue2: defaultValue, - - _defaultValue: defaultValue, - - // Used to track how many concurrent renderers this context currently - // supports within in a single renderer. Such as parallel server rendering. - _threadCount: 0, - _definitionLoaded: false, - // These are circular - Provider: (null: any), - displayName: globalName, - }; - - context.Provider = { - $$typeof: REACT_PROVIDER_TYPE, - _context: context, - }; - - if (__DEV__) { - context._currentRenderer = null; - context._currentRenderer2 = null; - } - ContextRegistry[globalName] = context; - return context; -} - -// This function is called by FlightClient to create a server context sent from -// the server. Its possible that FlightClient is creating it before the -// definition is loaded on the server. We'll create it with a null default value -// if thats the case and when the definition loads it will set the correct -// default value. -export function getOrCreateServerContext(globalName: string) { - if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = _createServerContext( - globalName, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, - ); - } - return ContextRegistry[globalName]; -} diff --git a/packages/react/src/React.js b/packages/react/src/React.js index d13bf62941b..a762d7d83f1 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -32,7 +32,6 @@ import { isValidElement, } from './ReactElement'; import {createContext} from './ReactContext'; -import {createServerContext} from './ReactServerContext'; import {lazy} from './ReactLazy'; import {forwardRef} from './ReactForwardRef'; import {memo} from './ReactMemo'; @@ -62,6 +61,7 @@ import { createFactoryWithValidation, cloneElementWithValidation, } from './ReactElementValidator'; +import {createServerContext} from 'shared/ReactServerContext'; import {createMutableSource} from './ReactMutableSource'; import ReactSharedInternals from './ReactSharedInternals'; import {startTransition} from './ReactStartTransition'; diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js deleted file mode 100644 index a236de4b2a4..00000000000 --- a/packages/react/src/ReactServerContext.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import { - REACT_PROVIDER_TYPE, - REACT_SERVER_CONTEXT_TYPE, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, -} from 'shared/ReactSymbols'; - -import type { - ReactServerContext, - ServerContextJSONValue, -} from 'shared/ReactTypes'; - -import ReactSharedInternals from 'shared/ReactSharedInternals'; - -import {enableServerContext} from 'shared/ReactFeatureFlags'; - -const ContextRegistry = ReactSharedInternals.ContextRegistry; - -export function createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - if (!enableServerContext) { - throw new Error('Not implemented.'); - } - let context; - if (ContextRegistry[globalName]) { - context = ContextRegistry[globalName]; - if ( - context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED - ) { - context._defaultValue = defaultValue; - } else { - throw new Error(`ServerContext: ${globalName} already defined`); - } - } else { - context = ContextRegistry[globalName] = _createServerContext( - globalName, - defaultValue, - ); - } - return context; -} - -function _createServerContext( - globalName: string, - defaultValue: T, -): ReactServerContext { - const context: ReactServerContext = { - $$typeof: REACT_SERVER_CONTEXT_TYPE, - // As a workaround to support multiple concurrent renderers, we categorize - // some renderers as primary and others as secondary. We only expect - // there to be two concurrent renderers at most: React Native (primary) and - // Fabric (secondary); React DOM (primary) and React ART (secondary). - // Secondary renderers store their context values on separate fields. - _currentValue: defaultValue, - _currentValue2: defaultValue, - - _defaultValue: defaultValue, - - // Used to track how many concurrent renderers this context currently - // supports within in a single renderer. Such as parallel server rendering. - _threadCount: 0, - _definitionLoaded: false, - // These are circular - Provider: (null: any), - displayName: globalName, - }; - - context.Provider = { - $$typeof: REACT_PROVIDER_TYPE, - _context: context, - }; - - if (__DEV__) { - context._currentRenderer = null; - context._currentRenderer2 = null; - } - ContextRegistry[globalName] = context; - return context; -} - -// This function is called by FlightClient to create a server context sent from -// the server. Its possible that FlightClient is creating it before the -// definition is loaded on the server. We'll create it with a null default value -// if thats the case and when the definition loads it will set the correct -// default value. -export function getOrCreateServerContext(globalName: string) { - if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = _createServerContext( - globalName, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, - ); - } - return ContextRegistry[globalName]; -} diff --git a/packages/shared/ReactServerContext.js b/packages/shared/ReactServerContext.js new file mode 100644 index 00000000000..b0a5e23b230 --- /dev/null +++ b/packages/shared/ReactServerContext.js @@ -0,0 +1,92 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import { + REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, +} from 'shared/ReactSymbols'; + +import type { + ReactServerContext, + ServerContextJSONValue, +} from 'shared/ReactTypes'; + +import {enableServerContext} from 'shared/ReactFeatureFlags'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; + +const ContextRegistry = ReactSharedInternals.ContextRegistry; + +export function createServerContext( + globalName: string, + defaultValue: T, +): ReactServerContext { + if (!enableServerContext) { + throw new Error('Not implemented.'); + } + let wasDefined = true; + if (!ContextRegistry[globalName]) { + wasDefined = false; + const context: ReactServerContext = { + $$typeof: REACT_SERVER_CONTEXT_TYPE, + + // As a workaround to support multiple concurrent renderers, we categorize + // some renderers as primary and others as secondary. We only expect + // there to be two concurrent renderers at most: React Native (primary) and + // Fabric (secondary); React DOM (primary) and React ART (secondary). + // Secondary renderers store their context values on separate fields. + _currentValue: defaultValue, + _currentValue2: defaultValue, + + _defaultValue: defaultValue, + + // Used to track how many concurrent renderers this context currently + // supports within in a single renderer. Such as parallel server rendering. + _threadCount: 0, + _definitionLoaded: false, + // These are circular + Provider: (null: any), + Consumer: (null: any), + _globalName: globalName, + }; + + context.Provider = { + $$typeof: REACT_PROVIDER_TYPE, + _context: context, + }; + + if (__DEV__) { + let hasWarnedAboutUsingConsumer; + context._currentRenderer = null; + context._currentRenderer2 = null; + Object.defineProperties( + context, + ({ + Consumer: { + get() { + console.error( + 'Consumer pattern is not supported by ReactServerContext', + ); + return null; + }, + }, + }: any), + ); + } + ContextRegistry[globalName] = context; + } + + const context = ContextRegistry[globalName]; + if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + context._defaultValue = defaultValue; + } else if (wasDefined) { + throw new Error(`ServerContext: ${globalName} already defined`); + } + return context; +} diff --git a/packages/shared/ReactServerContextRegistry.js b/packages/shared/ReactServerContextRegistry.js new file mode 100644 index 00000000000..d721a3750ad --- /dev/null +++ b/packages/shared/ReactServerContextRegistry.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; +import {createServerContext} from './ReactServerContext'; + +const ContextRegistry = ReactSharedInternals.ContextRegistry; + +export function getOrCreateServerContext(globalName: string) { + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = createServerContext( + globalName, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, + ); + } + return ContextRegistry[globalName]; +} diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 5432815232d..901e95f99b1 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -12,8 +12,8 @@ export type ReactNode = | ReactPortal | ReactText | ReactFragment - | ReactProvider - | ReactConsumer; + | ReactProvider> + | ReactConsumer>; export type ReactEmpty = null | void | boolean; @@ -25,7 +25,7 @@ export type ReactText = string | number; export type ReactProvider = { $$typeof: Symbol | number, - type: ReactProviderType | ReactServerProviderType, + type: ReactProviderType, key: null | string, ref: null, props: { @@ -84,17 +84,10 @@ export type ServerContextJSONValue = | $ReadOnlyArray | {+[key: string]: ServerContextJSONValue}; -export type ReactServerContext = { - $$typeof: Symbol | number, - Provider: ReactServerProviderType, +export type ReactServerContext = ReactContext & { _defaultValue: T, - _currentValue: T, - _currentValue2: T, - _currentRenderer?: Object | null, - _currentRenderer2?: Object | null, - _threadCount: number, _definitionLoaded: boolean, - +displayName: string, + _globalName: string, ... }; diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 9a1cee36a13..d07a1b35266 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -45,7 +45,7 @@ function getWrappedName( } // Keep in sync with react-reconciler/getComponentNameFromFiber -function getContextName(type: ReactContext | ReactServerContext) { +function getContextName(type: ReactContext) { return type.displayName || 'Context'; } diff --git a/scripts/jest/matchers/toWarnDev.js b/scripts/jest/matchers/toWarnDev.js index dd395d30d11..5e2a144b4e2 100644 --- a/scripts/jest/matchers/toWarnDev.js +++ b/scripts/jest/matchers/toWarnDev.js @@ -86,9 +86,7 @@ const createMatcherFor = (consoleMethod, matcherName) => // doesn't match the number of arguments. // We'll fail the test if it happens. let argIndex = 0; - if (format.replace) { - format.replace(/%s/g, () => argIndex++); - } + format.replace(/%s/g, () => argIndex++); if (argIndex !== args.length) { lastWarningWithMismatchingFormat = { format, From bc039977382a2260a611a20875208c69092ae4ea Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 13:36:19 -0500 Subject: [PATCH 51/83] remove unions --- .../react-devtools-shared/src/backend/types.js | 2 +- .../src/server/ReactPartialRendererHooks.js | 4 +--- .../react-reconciler/src/ReactFiberHooks.new.js | 14 +++++++------- .../react-reconciler/src/ReactFiberHooks.old.js | 14 +++++++------- .../src/ReactFiberNewContext.new.js | 12 +++++------- .../src/ReactFiberNewContext.old.js | 12 +++++------- .../react-reconciler/src/ReactFiberScope.new.js | 8 +++----- .../react-reconciler/src/ReactFiberScope.old.js | 8 +++----- .../react-reconciler/src/ReactInternalTypes.js | 4 ++-- packages/react-server/src/ReactFizzHooks.js | 4 +--- packages/react-server/src/ReactFizzNewContext.js | 12 ++++-------- packages/react-server/src/ReactFlightHooks.js | 4 +--- packages/shared/ReactTypes.js | 4 ++-- 13 files changed, 42 insertions(+), 60 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index 19327019e59..ec6b09d255e 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -89,7 +89,7 @@ export type FindNativeNodesForFiberID = (id: number) => ?Array; export type ReactProviderType = { $$typeof: Symbol | number, - _context: ReactContext | ReactServerContext, + _context: ReactContext, ... }; diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index e2280fbd48b..57c63f402e0 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -223,9 +223,7 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext( - context: ReactContext | ReactServerContext, -): T { +function readContext(context: ReactContext): T { const threadID = currentPartialRenderer.threadID; validateContextBounds(context, threadID); if (__DEV__) { diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index f04e17f74fb..d3876668f93 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -2539,7 +2539,7 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2687,7 +2687,7 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2829,7 +2829,7 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2971,7 +2971,7 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, @@ -3114,7 +3114,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3273,7 +3273,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3432,7 +3432,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 7f55803957a..c119a648223 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -2539,7 +2539,7 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2687,7 +2687,7 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2829,7 +2829,7 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2971,7 +2971,7 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { return readContext(context); }, @@ -3114,7 +3114,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3273,7 +3273,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3432,7 +3432,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext | ReactServerContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 13b75a56391..350b13f86dd 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -90,7 +90,7 @@ export function exitDisallowedContextReadInDEV(): void { export function pushProvider( providerFiber: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, nextValue: T, ): void { if (isPrimaryRenderer) { @@ -195,7 +195,7 @@ export function scheduleContextWorkOnParentPath( export function propagateContextChange( workInProgress: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, renderLanes: Lanes, ): void { if (enableLazyContextPropagation) { @@ -216,7 +216,7 @@ export function propagateContextChange( function propagateContextChange_eager( workInProgress: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, renderLanes: Lanes, ): void { // Only used by eager implementation @@ -383,7 +383,7 @@ function propagateContextChanges( const dependency = dep; const consumer = fiber; findContext: for (let i = 0; i < contexts.length; i++) { - const context: ReactContext | ReactServerContext = contexts[i]; + const context: ReactContext = contexts[i]; // Check if the context matches. // TODO: Compare selected values to bail out early. if (dependency.context === context) { @@ -653,9 +653,7 @@ export function prepareToReadContext( } } -export function readContext( - context: ReactContext | ReactServerContext, -): T { +export function readContext(context: ReactContext): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 0909254b6f5..c61e516ac94 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -90,7 +90,7 @@ export function exitDisallowedContextReadInDEV(): void { export function pushProvider( providerFiber: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, nextValue: T, ): void { if (isPrimaryRenderer) { @@ -195,7 +195,7 @@ export function scheduleContextWorkOnParentPath( export function propagateContextChange( workInProgress: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, renderLanes: Lanes, ): void { if (enableLazyContextPropagation) { @@ -216,7 +216,7 @@ export function propagateContextChange( function propagateContextChange_eager( workInProgress: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, renderLanes: Lanes, ): void { // Only used by eager implementation @@ -383,7 +383,7 @@ function propagateContextChanges( const dependency = dep; const consumer = fiber; findContext: for (let i = 0; i < contexts.length; i++) { - const context: ReactContext | ReactServerContext = contexts[i]; + const context: ReactContext = contexts[i]; // Check if the context matches. // TODO: Compare selected values to bail out early. if (dependency.context === context) { @@ -653,9 +653,7 @@ export function prepareToReadContext( } } -export function readContext( - context: ReactContext | ReactServerContext, -): T { +export function readContext(context: ReactContext): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. diff --git a/packages/react-reconciler/src/ReactFiberScope.new.js b/packages/react-reconciler/src/ReactFiberScope.new.js index 491e82d89d3..9455af19c7f 100644 --- a/packages/react-reconciler/src/ReactFiberScope.new.js +++ b/packages/react-reconciler/src/ReactFiberScope.new.js @@ -111,7 +111,7 @@ function collectFirstScopedNodeFromChildren( function collectNearestContextValues( node: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, childContextValues: Array, ): void { if (node.tag === ContextProvider && node.type._context === context) { @@ -131,7 +131,7 @@ function collectNearestContextValues( function collectNearestChildContextValues( startingChild: Fiber | null, - context: ReactContext | ReactServerContext, + context: ReactContext, childContextValues: Array, ): void { let child = startingChild; @@ -177,9 +177,7 @@ function containsNode(node: Object): boolean { return false; } -function getChildContextValues( - context: ReactContext | ReactServerContext, -): Array { +function getChildContextValues(context: ReactContext): Array { const currentFiber = getInstanceFromScope(this); if (currentFiber === null) { return []; diff --git a/packages/react-reconciler/src/ReactFiberScope.old.js b/packages/react-reconciler/src/ReactFiberScope.old.js index 491e82d89d3..9455af19c7f 100644 --- a/packages/react-reconciler/src/ReactFiberScope.old.js +++ b/packages/react-reconciler/src/ReactFiberScope.old.js @@ -111,7 +111,7 @@ function collectFirstScopedNodeFromChildren( function collectNearestContextValues( node: Fiber, - context: ReactContext | ReactServerContext, + context: ReactContext, childContextValues: Array, ): void { if (node.tag === ContextProvider && node.type._context === context) { @@ -131,7 +131,7 @@ function collectNearestContextValues( function collectNearestChildContextValues( startingChild: Fiber | null, - context: ReactContext | ReactServerContext, + context: ReactContext, childContextValues: Array, ): void { let child = startingChild; @@ -177,9 +177,7 @@ function containsNode(node: Object): boolean { return false; } -function getChildContextValues( - context: ReactContext | ReactServerContext, -): Array { +function getChildContextValues(context: ReactContext): Array { const currentFiber = getInstanceFromScope(this); if (currentFiber === null) { return []; diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index aace3ae0081..243b524315b 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -50,7 +50,7 @@ export type HookType = | 'useCacheRefresh'; export type ContextDependency = { - context: ReactContext | ReactServerContext, + context: ReactContext, next: ContextDependency | null, memoizedValue: T, ... @@ -343,7 +343,7 @@ type Dispatch
= A => void; export type Dispatcher = {| getCacheSignal?: () => AbortSignal, getCacheForType?: (resourceType: () => T) => T, - readContext(context: ReactContext | ReactServerContext): T, + readContext(context: ReactContext): T, useState(initialState: (() => S) | S): [S, Dispatch>], useReducer( reducer: (S, A) => S, diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index e595fb2a82f..3bfdd9d50cd 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -244,9 +244,7 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext( - context: ReactContext | ReactServerContext, -): T { +function readContext(context: ReactContext): T { if (__DEV__) { if (isInHookUserCodeInDev) { console.error( diff --git a/packages/react-server/src/ReactFizzNewContext.js b/packages/react-server/src/ReactFizzNewContext.js index c333b5681de..f65a065367f 100644 --- a/packages/react-server/src/ReactFizzNewContext.js +++ b/packages/react-server/src/ReactFizzNewContext.js @@ -23,7 +23,7 @@ if (__DEV__) { type ContextNode = { parent: null | ContextNode, depth: number, // Short hand to compute the depth of the tree at this node. - context: ReactContext | ReactServerContext, + context: ReactContext, parentValue: T, value: T, }; @@ -179,7 +179,7 @@ export function switchContext(newSnapshot: ContextSnapshot): void { } export function pushProvider( - context: ReactContext | ReactServerContext, + context: ReactContext, nextValue: T, ): ContextSnapshot { let prevValue; @@ -228,9 +228,7 @@ export function pushProvider( return newNode; } -export function popProvider( - context: ReactContext | ReactServerContext, -): ContextSnapshot { +export function popProvider(context: ReactContext): ContextSnapshot { const prevSnapshot = currentActiveSnapshot; if (prevSnapshot === null) { @@ -298,9 +296,7 @@ export function getActiveContext(): ContextSnapshot { return currentActiveSnapshot; } -export function readContext( - context: ReactContext | ReactServerContext, -): T { +export function readContext(context: ReactContext): T { const value = isPrimaryRenderer ? context._currentValue : context._currentValue2; diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index 0b12e00bfa3..f8c5c833cab 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -16,9 +16,7 @@ import type { import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; import {readContext as readContextImpl} from './ReactFlightNewContext'; -function readContext( - context: ReactContext | ReactServerContext, -): T { +function readContext(context: ReactContext): T { if (__DEV__) { if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { console.error('Only ServerContext is supported in Flight'); diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 901e95f99b1..47a2adc38e4 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -38,7 +38,7 @@ export type ReactProvider = { export type ReactProviderType = { $$typeof: Symbol | number, - _context: ReactContext | ReactServerContext, + _context: ReactContext, ... }; @@ -50,7 +50,7 @@ export type ReactServerProviderType = { export type ReactConsumer = { $$typeof: Symbol | number, - type: ReactContext | ReactServerContext, + type: ReactContext, key: null | string, ref: null, props: { From 499f20b8bbb92b5f52e896e5a91293cfff57f363 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 13:41:42 -0500 Subject: [PATCH 52/83] Lint --- packages/react-debug-tools/src/ReactDebugHooks.js | 1 - packages/react-devtools-shared/src/backend/types.js | 6 +----- packages/react-dom/src/server/ReactPartialRenderer.js | 6 +----- .../react-dom/src/server/ReactPartialRendererContext.js | 2 +- .../react-dom/src/server/ReactPartialRendererHooks.js | 1 - packages/react-reconciler/src/ReactFiberBeginWork.new.js | 6 +----- packages/react-reconciler/src/ReactFiberBeginWork.old.js | 6 +----- packages/react-reconciler/src/ReactFiberScope.new.js | 1 - packages/react-reconciler/src/ReactFiberScope.old.js | 1 - packages/react-reconciler/src/ReactInternalTypes.js | 1 - .../react-reconciler/src/getComponentNameFromFiber.js | 6 +----- packages/react-server/src/ReactFlightServer.js | 9 +-------- packages/shared/ReactServerContext.js | 9 ++++++--- packages/shared/getComponentNameFromType.js | 6 +----- 14 files changed, 14 insertions(+), 47 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index c9f463471bf..ef07b78c783 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -12,7 +12,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, ReactProviderType, StartTransitionOptions, ServerContextJSONValue, diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index ec6b09d255e..96261a03bfd 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactContext, - ReactServerContext, - Wakeable, -} from 'shared/ReactTypes'; +import type {ReactContext, Wakeable} from 'shared/ReactTypes'; import type {Source} from 'shared/ReactElementType'; import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; import type { diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index f7156a50535..3ac5eb0d5d4 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -10,11 +10,7 @@ import type {ThreadID} from './ReactThreadIDAllocator'; import type {ReactElement} from 'shared/ReactElementType'; import type {LazyComponent} from 'react/src/ReactLazy'; -import type { - ReactProvider, - ReactContext, - ReactServerContext, -} from 'shared/ReactTypes'; +import type {ReactProvider, ReactContext} from 'shared/ReactTypes'; import * as React from 'react'; import isArray from 'shared/isArray'; diff --git a/packages/react-dom/src/server/ReactPartialRendererContext.js b/packages/react-dom/src/server/ReactPartialRendererContext.js index f51dcb27b14..65af591e365 100644 --- a/packages/react-dom/src/server/ReactPartialRendererContext.js +++ b/packages/react-dom/src/server/ReactPartialRendererContext.js @@ -8,7 +8,7 @@ */ import type {ThreadID} from './ReactThreadIDAllocator'; -import type {ReactContext, ReactServerContext} from 'shared/ReactTypes'; +import type {ReactContext} from 'shared/ReactTypes'; import {disableLegacyContext} from 'shared/ReactFeatureFlags'; import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols'; diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 57c63f402e0..eb1eb5c3dca 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -14,7 +14,6 @@ import type { MutableSourceGetSnapshotFn, MutableSourceSubscribeFn, ReactContext, - ReactServerContext, } from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index ee97b8d4838..09a0e16a1e5 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactProviderType, - ReactContext, - ReactServerContext, -} from 'shared/ReactTypes'; +import type {ReactProviderType, ReactContext} from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {TypeOfMode} from './ReactTypeOfMode'; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 9de275b9cfa..2db03f39a2c 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactProviderType, - ReactContext, - ReactServerContext, -} from 'shared/ReactTypes'; +import type {ReactProviderType, ReactContext} from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {TypeOfMode} from './ReactTypeOfMode'; diff --git a/packages/react-reconciler/src/ReactFiberScope.new.js b/packages/react-reconciler/src/ReactFiberScope.new.js index 9455af19c7f..9fe9d09572a 100644 --- a/packages/react-reconciler/src/ReactFiberScope.new.js +++ b/packages/react-reconciler/src/ReactFiberScope.new.js @@ -12,7 +12,6 @@ import type { ReactScopeInstance, ReactContext, ReactScopeQuery, - ReactServerContext, } from 'shared/ReactTypes'; import { diff --git a/packages/react-reconciler/src/ReactFiberScope.old.js b/packages/react-reconciler/src/ReactFiberScope.old.js index 9455af19c7f..9fe9d09572a 100644 --- a/packages/react-reconciler/src/ReactFiberScope.old.js +++ b/packages/react-reconciler/src/ReactFiberScope.old.js @@ -12,7 +12,6 @@ import type { ReactScopeInstance, ReactContext, ReactScopeQuery, - ReactServerContext, } from 'shared/ReactTypes'; import { diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 243b524315b..5e6096c6348 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -11,7 +11,6 @@ import type {Source} from 'shared/ReactElementType'; import type { RefObject, ReactContext, - ReactServerContext, MutableSourceSubscribeFn, MutableSourceGetSnapshotFn, MutableSourceVersion, diff --git a/packages/react-reconciler/src/getComponentNameFromFiber.js b/packages/react-reconciler/src/getComponentNameFromFiber.js index dec6c3d5caf..5cb87189c97 100644 --- a/packages/react-reconciler/src/getComponentNameFromFiber.js +++ b/packages/react-reconciler/src/getComponentNameFromFiber.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactContext, - ReactServerContext, - ReactProviderType, -} from 'shared/ReactTypes'; +import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; import { FunctionComponent, diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 749c91b2485..1901a46dc85 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -16,10 +16,7 @@ import type { ModuleKey, } from './ReactFlightServerConfig'; import type {ContextSnapshot} from './ReactFlightNewContext'; -import type { - ReactServerContext, - ServerContextJSONValue, -} from 'shared/ReactTypes'; +import type {ServerContextJSONValue} from 'shared/ReactTypes'; import { scheduleWork, @@ -55,15 +52,12 @@ import { REACT_MEMO_TYPE, REACT_PROVIDER_TYPE, REACT_SERVER_CONTEXT_TYPE, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, } from 'shared/ReactSymbols'; import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import isArray from 'shared/isArray'; -import {createServerContext} from 'react'; - type ReactJSONValue = | string | boolean @@ -113,7 +107,6 @@ export type Options = { }; const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; -const ContextRegistry = ReactSharedInternals.ContextRegistry; function defaultErrorHandler(error: mixed) { console['error'](error); diff --git a/packages/shared/ReactServerContext.js b/packages/shared/ReactServerContext.js index b0a5e23b230..9b1d9120e71 100644 --- a/packages/shared/ReactServerContext.js +++ b/packages/shared/ReactServerContext.js @@ -70,9 +70,12 @@ export function createServerContext( ({ Consumer: { get() { - console.error( - 'Consumer pattern is not supported by ReactServerContext', - ); + if (!hasWarnedAboutUsingConsumer) { + console.error( + 'Consumer pattern is not supported by ReactServerContext', + ); + hasWarnedAboutUsingConsumer = true; + } return null; }, }, diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index d07a1b35266..36432e56aca 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -8,11 +8,7 @@ */ import type {LazyComponent} from 'react/src/ReactLazy'; -import type { - ReactContext, - ReactServerContext, - ReactProviderType, -} from 'shared/ReactTypes'; +import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; import { REACT_CONTEXT_TYPE, From b5e5e4770a3b159c14a48654c488870d13b261fb Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 14:28:11 -0500 Subject: [PATCH 53/83] fix oopsies (tests/lint/mismatching arguments/signatures --- .../src/__tests__/ReactDOMFizzServer-test.js | 1 - .../src/ReactNoopFlightServer.js | 2 +- .../src/ReactFlightDOMServerBrowser.js | 7 ++++- .../src/ReactFlightDOMServerNode.js | 7 ++++- .../ReactFlightNativeRelayServerHostConfig.js | 1 + .../react-server/src/ReactFlightServer.js | 26 ++++++++++--------- packages/shared/ReactServerContext.js | 10 +++++++ packages/shared/ReactTypes.js | 4 +-- 8 files changed, 40 insertions(+), 18 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index ce6121ca926..a85485dd5c0 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -2461,7 +2461,6 @@ describe('ReactDOMFizzServer', () => { let ServerContext; function inlineLazyServerContextInitialization() { if (!ServerContext) { - console.log({ServerContext}); ServerContext = React.createServerContext('ServerContext', 'default'); } return ServerContext; diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index 8a1ad32a2a9..c611b0a92f4 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -80,7 +80,7 @@ function render( const request = ReactNoopFlightServer.createRequest( model, bundlerConfig, - options, + options ? options.onError : undefined, context, ); ReactNoopFlightServer.startWork(request); diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js index a9cc8e50914..aeee2d24806 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js @@ -27,7 +27,12 @@ function renderToReadableStream( options?: Options, context?: Array<[string, ServerContextJSONValue]>, ): ReadableStream { - const request = createRequest(model, webpackMap, options, context); + const request = createRequest( + model, + webpackMap, + options ? options.onError : undefined, + context, + ); const stream = new ReadableStream({ type: 'bytes', start(controller) { diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js index bb990c9e179..c088725f259 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js @@ -36,7 +36,12 @@ function renderToPipeableStream( options?: Options, context?: Array<[string, ServerContextJSONValue]>, ): Controls { - const request = createRequest(model, webpackMap, options, context); + const request = createRequest( + model, + webpackMap, + options ? options.onError : undefined, + context, + ); let hasStartedFlowing = false; startWork(request); return { diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js index 980652b6890..0387d94ecad 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js @@ -103,6 +103,7 @@ function convertModelToJSON( } return json; } + export function processModelChunk( request: Request, id: number, diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 1901a46dc85..e17ae9ba280 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -16,7 +16,10 @@ import type { ModuleKey, } from './ReactFlightServerConfig'; import type {ContextSnapshot} from './ReactFlightNewContext'; -import type {ServerContextJSONValue} from 'shared/ReactTypes'; +import type { + ServerContextJSONValue, + ReactServerContext, +} from 'shared/ReactTypes'; import { scheduleWork, @@ -120,11 +123,10 @@ const CLOSED = 2; export function createRequest( model: ReactModel, bundlerConfig: BundlerConfig, - options?: Options, + onError: void | ((error: mixed) => void), context?: Array<[string, ServerContextJSONValue]>, ): Request { const pingedSegments = []; - const onError = options ? options.onError : undefined; const request = { status: OPEN, fatalError: null, @@ -211,7 +213,7 @@ function attemptResolveElement( return true; }); if (extraKeys.length !== 0) { - throw new Error( + console.error( 'ServerContext can only have a value prop and children. Found: ' + JSON.stringify(extraKeys), ); @@ -219,7 +221,7 @@ function attemptResolveElement( } return [ REACT_PROVIDER_TYPE, - type._context.displayName, + type._context._globalName, key, // Rely on __popProvider being serialized last to pop the provider. {...props, __popProvider$$: type._context}, @@ -513,6 +515,7 @@ export function resolveModelToJSON( } if ( + value && value.$$typeof === REACT_SERVER_CONTEXT_TYPE && key === '__popProvider$$' ) { @@ -894,18 +897,17 @@ export function startFlowing(request: Request, destination: Destination): void { function importServerContexts( contexts?: Array<[string, ServerContextJSONValue]>, ) { - const prevContext = getActiveContext(); - switchContext(rootContextSnapshot); - const registry: {[name: string]: ReactServerContext} = {}; if (contexts) { + const prevContext = getActiveContext(); + switchContext(rootContextSnapshot); for (let i = 0; i < contexts.length; i++) { const [name, value] = contexts[i]; const context = getOrCreateServerContext(name); pushProvider(context, value); - registry[name] = context; } + const importedContext = getActiveContext(); + switchContext(prevContext); + return importedContext; } - const importedContext = getActiveContext(); - switchContext(prevContext); - return importedContext; + return rootContextSnapshot; } diff --git a/packages/shared/ReactServerContext.js b/packages/shared/ReactServerContext.js index 9b1d9120e71..a45c56afe7d 100644 --- a/packages/shared/ReactServerContext.js +++ b/packages/shared/ReactServerContext.js @@ -88,6 +88,16 @@ export function createServerContext( const context = ContextRegistry[globalName]; if (context._defaultValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { context._defaultValue = defaultValue; + if ( + context._currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { + context._currentValue = defaultValue; + } + if ( + context._currentValue2 === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { + context._currentValue2 = defaultValue; + } } else if (wasDefined) { throw new Error(`ServerContext: ${globalName} already defined`); } diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 47a2adc38e4..a3fa804df3e 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -12,8 +12,8 @@ export type ReactNode = | ReactPortal | ReactText | ReactFragment - | ReactProvider> - | ReactConsumer>; + | ReactProvider + | ReactConsumer; export type ReactEmpty = null | void | boolean; From 90f6f08afd7dceaaa1cde2c2a966a9a4e17e292d Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 14:30:28 -0500 Subject: [PATCH 54/83] lint again --- packages/react-server/src/ReactFlightServer.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index e17ae9ba280..73849b218b3 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -16,10 +16,7 @@ import type { ModuleKey, } from './ReactFlightServerConfig'; import type {ContextSnapshot} from './ReactFlightNewContext'; -import type { - ServerContextJSONValue, - ReactServerContext, -} from 'shared/ReactTypes'; +import type {ServerContextJSONValue} from 'shared/ReactTypes'; import { scheduleWork, @@ -214,8 +211,8 @@ function attemptResolveElement( }); if (extraKeys.length !== 0) { console.error( - 'ServerContext can only have a value prop and children. Found: ' + - JSON.stringify(extraKeys), + 'ServerContext can only have a value prop and children. Found: %s', + JSON.stringify(extraKeys), ); } } From 672712e2a1762ebec032b057eb89b0288b23d01d Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 14:41:50 -0500 Subject: [PATCH 55/83] replace-fork --- packages/react-reconciler/src/ReactFiberWorkLoop.old.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 9d8528b75fe..d8bb6b16e29 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -1555,6 +1555,7 @@ function handleError(root, thrownValue): void { ); } } + throwException( root, erroredWork.return, From 03fb89b00b6798316524095df5fd40ca2add8a10 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Wed, 2 Mar 2022 14:45:46 -0500 Subject: [PATCH 56/83] remove extraneous change --- packages/react-noop-renderer/src/ReactNoopFlightClient.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-noop-renderer/src/ReactNoopFlightClient.js b/packages/react-noop-renderer/src/ReactNoopFlightClient.js index b4e2e830fc8..df586c6efb2 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightClient.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightClient.js @@ -40,8 +40,7 @@ function read(source: Source): T { processStringChunk(response, source[i], 0); } close(response); - const root = response.readRoot(); - return root; + return response.readRoot(); } export {read}; From db992af86e25284911b758a2c92059a82bdbfeba Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 12:58:56 -0500 Subject: [PATCH 57/83] rebase --- packages/react-reconciler/src/ReactFiberHooks.new.js | 1 - packages/react-reconciler/src/ReactFiberHooks.old.js | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index d3876668f93..6af6ff99dc5 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -15,7 +15,6 @@ import type { StartTransitionOptions, ReactServerContext, ServerContextJSONValue, - ReactServerContext, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.new'; diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index c119a648223..c1c4ca2e29f 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -15,7 +15,6 @@ import type { StartTransitionOptions, ReactServerContext, ServerContextJSONValue, - ReactServerContext, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.old'; From 9ceb9553b3eb53aa488374f6f62cf91773660ec3 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 13:04:05 -0500 Subject: [PATCH 58/83] reinline --- .../react-server/src/ReactFlightServer.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 73849b218b3..e836351fc6c 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -417,14 +417,6 @@ function describeObjectForErrorMessage( } } -function isReactElement(value: mixed) { - return ( - typeof value === 'object' && - value !== null && - (value: any).$$typeof === REACT_ELEMENT_TYPE - ); -} - let insideContextProps = null; let isInsideContextValue = false; @@ -470,7 +462,11 @@ export function resolveModelToJSON( } // Resolve server components. - while (isReactElement(value)) { + while ( + typeof value === 'object' && + value !== null && + (value: any).$$typeof === REACT_ELEMENT_TYPE + ) { if (__DEV__) { if (isInsideContextValue) { console.error('React elements are not allowed in ServerContext'); @@ -749,7 +745,11 @@ function retrySegment(request: Request, segment: Segment): void { switchContext(segment.context); try { let value = segment.model; - while (isReactElement(value)) { + while ( + typeof value === 'object' && + value !== null && + (value: any).$$typeof === REACT_ELEMENT_TYPE + ) { // TODO: Concatenate keys of parents onto children. const element: React$Element = (value: any); // Attempt to render the server component. From eafb265996874f6ef9b3ffec2f5f0ff332277cb2 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 13:14:04 -0500 Subject: [PATCH 59/83] rebase --- packages/react/src/React.js | 2 +- packages/{shared => react/src}/ReactServerContext.js | 0 packages/shared/ReactServerContextRegistry.js | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename packages/{shared => react/src}/ReactServerContext.js (100%) diff --git a/packages/react/src/React.js b/packages/react/src/React.js index a762d7d83f1..264c1e1dc56 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -61,7 +61,7 @@ import { createFactoryWithValidation, cloneElementWithValidation, } from './ReactElementValidator'; -import {createServerContext} from 'shared/ReactServerContext'; +import {createServerContext} from './ReactServerContext'; import {createMutableSource} from './ReactMutableSource'; import ReactSharedInternals from './ReactSharedInternals'; import {startTransition} from './ReactStartTransition'; diff --git a/packages/shared/ReactServerContext.js b/packages/react/src/ReactServerContext.js similarity index 100% rename from packages/shared/ReactServerContext.js rename to packages/react/src/ReactServerContext.js diff --git a/packages/shared/ReactServerContextRegistry.js b/packages/shared/ReactServerContextRegistry.js index d721a3750ad..ab8421b3c32 100644 --- a/packages/shared/ReactServerContextRegistry.js +++ b/packages/shared/ReactServerContextRegistry.js @@ -9,7 +9,7 @@ import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import {createServerContext} from './ReactServerContext'; +import {createServerContext} from 'react'; const ContextRegistry = ReactSharedInternals.ContextRegistry; From 5b937ad15b5481717b67be993117bbdccc93808e Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 13:30:42 -0500 Subject: [PATCH 60/83] add back changes lost due to rebase being hard --- .../react-debug-tools/src/ReactDebugHooks.js | 1 - .../src/backend/ReactSymbols.js | 2 -- .../src/backend/renderer.js | 2 -- .../src/ReactNoopFlightServer.js | 2 +- .../src/ReactFiberCacheComponent.new.js | 2 ++ .../src/ReactFiberCacheComponent.old.js | 2 ++ .../src/ReactFiberHooks.new.js | 3 --- .../src/ReactFiberHooks.old.js | 3 --- packages/react-server/src/ReactFizzHooks.js | 1 - .../react-server/src/ReactFlightServer.js | 4 +-- packages/react/src/ReactContext.js | 4 +++ packages/react/src/ReactHooks.js | 2 -- packages/react/src/ReactServerContext.js | 1 - packages/shared/ReactTypes.js | 11 ++++---- .../forks/ReactFeatureFlags.test-renderer.js | 2 +- .../shared/forks/ReactFeatureFlags.testing.js | 2 +- scripts/error-codes/codes.json | 27 ++++++++++--------- 17 files changed, 32 insertions(+), 39 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index ef07b78c783..74f874ad292 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -14,7 +14,6 @@ import type { ReactContext, ReactProviderType, StartTransitionOptions, - ServerContextJSONValue, } from 'shared/ReactTypes'; import type { Fiber, diff --git a/packages/react-devtools-shared/src/backend/ReactSymbols.js b/packages/react-devtools-shared/src/backend/ReactSymbols.js index cbeb1b9c918..775ad34e772 100644 --- a/packages/react-devtools-shared/src/backend/ReactSymbols.js +++ b/packages/react-devtools-shared/src/backend/ReactSymbols.js @@ -19,7 +19,6 @@ export const CONCURRENT_MODE_SYMBOL_STRING = 'Symbol(react.concurrent_mode)'; export const CONTEXT_NUMBER = 0xeace; export const CONTEXT_SYMBOL_STRING = 'Symbol(react.context)'; -export const SERVER_CONTEXT_NUMBER = 0xeacf; export const SERVER_CONTEXT_SYMBOL_STRING = 'Symbol(react.server_context)'; export const DEPRECATED_ASYNC_MODE_SYMBOL_STRING = 'Symbol(react.async_mode)'; @@ -64,6 +63,5 @@ export const SUSPENSE_SYMBOL_STRING = 'Symbol(react.suspense)'; export const SUSPENSE_LIST_NUMBER = 0xead8; export const SUSPENSE_LIST_SYMBOL_STRING = 'Symbol(react.suspense_list)'; -export const SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED_NUMBER = 0xeae6; export const SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED_SYMBOL_STRING = 'Symbol(react.server_context.defaultValue)'; diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js index 70225e8281c..665b85d183a 100644 --- a/packages/react-devtools-shared/src/backend/renderer.js +++ b/packages/react-devtools-shared/src/backend/renderer.js @@ -85,7 +85,6 @@ import { FORWARD_REF_SYMBOL_STRING, MEMO_NUMBER, MEMO_SYMBOL_STRING, - SERVER_CONTEXT_NUMBER, SERVER_CONTEXT_SYMBOL_STRING, } from './ReactSymbols'; import {format} from './utils'; @@ -513,7 +512,6 @@ export function getInternalReactConstants( return `${resolvedContext.displayName || 'Context'}.Provider`; case CONTEXT_NUMBER: case CONTEXT_SYMBOL_STRING: - case SERVER_CONTEXT_NUMBER: case SERVER_CONTEXT_SYMBOL_STRING: // 16.3-16.5 read from "type" because the Consumer is the actual context object. // 16.6+ should read from "type._context" because Consumer can be different (in DEV). diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index c49b0f2b181..32d49b105f4 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -27,7 +27,7 @@ const ReactNoopFlightServer = ReactFlightServer({ callback(); }, beginWriting(destination: Destination): void {}, - writeChunk(destination: Destination, chunk: string): boolean { + writeChunk(destination: Destination, chunk: string): void { destination.push(chunk); }, writeChunkAndReturn(destination: Destination, chunk: string): boolean { diff --git a/packages/react-reconciler/src/ReactFiberCacheComponent.new.js b/packages/react-reconciler/src/ReactFiberCacheComponent.new.js index 14ec05bd5b4..3c6c851218a 100644 --- a/packages/react-reconciler/src/ReactFiberCacheComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberCacheComponent.new.js @@ -48,6 +48,8 @@ export const CacheContext: ReactContext = enableCache _currentValue: (null: any), _currentValue2: (null: any), _threadCount: 0, + _defaultValue: (undefined: any), + _globalName: (undefined: any), } : (null: any); diff --git a/packages/react-reconciler/src/ReactFiberCacheComponent.old.js b/packages/react-reconciler/src/ReactFiberCacheComponent.old.js index a34de4142e4..dcc83785117 100644 --- a/packages/react-reconciler/src/ReactFiberCacheComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberCacheComponent.old.js @@ -48,6 +48,8 @@ export const CacheContext: ReactContext = enableCache _currentValue: (null: any), _currentValue2: (null: any), _threadCount: 0, + _defaultValue: (undefined: any), + _globalName: (undefined: any), } : (null: any); diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 6af6ff99dc5..ebc35122d3b 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -13,8 +13,6 @@ import type { MutableSourceSubscribeFn, ReactContext, StartTransitionOptions, - ReactServerContext, - ServerContextJSONValue, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.new'; @@ -35,7 +33,6 @@ import { enableSuspenseLayoutEffectSemantics, enableUseMutableSource, enableTransitionTracing, - enableServerContext, } from 'shared/ReactFeatureFlags'; import { diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index c1c4ca2e29f..0da9f32eaac 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -13,8 +13,6 @@ import type { MutableSourceSubscribeFn, ReactContext, StartTransitionOptions, - ReactServerContext, - ServerContextJSONValue, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.old'; @@ -35,7 +33,6 @@ import { enableSuspenseLayoutEffectSemantics, enableUseMutableSource, enableTransitionTracing, - enableServerContext, } from 'shared/ReactFeatureFlags'; import { diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index 3bfdd9d50cd..ab740b172f1 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -15,7 +15,6 @@ import type { MutableSourceSubscribeFn, ReactContext, StartTransitionOptions, - ReactServerContext, } from 'shared/ReactTypes'; import type {ResponseState} from './ReactServerFormatConfig'; diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index e836351fc6c..0854ae1b402 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -221,7 +221,7 @@ function attemptResolveElement( type._context._globalName, key, // Rely on __popProvider being serialized last to pop the provider. - {...props, __popProvider$$: type._context}, + {value: props.value, children: props.children, __pop: type._context}, ]; } } @@ -510,7 +510,7 @@ export function resolveModelToJSON( if ( value && value.$$typeof === REACT_SERVER_CONTEXT_TYPE && - key === '__popProvider$$' + key === '__pop' ) { popProvider((value: any)); if (__DEV__) { diff --git a/packages/react/src/ReactContext.js b/packages/react/src/ReactContext.js index 41065c13ef0..374d4d3126d 100644 --- a/packages/react/src/ReactContext.js +++ b/packages/react/src/ReactContext.js @@ -30,6 +30,10 @@ export function createContext(defaultValue: T): ReactContext { // These are circular Provider: (null: any), Consumer: (null: any), + + // Add these to use same hidden class in VM as ServerContext + _defaultValue: (undefined: any), + _globalName: (undefined: any), }; context.Provider = { diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index bfcaf850c69..9dc7a98589e 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -14,8 +14,6 @@ import type { MutableSourceSubscribeFn, ReactContext, StartTransitionOptions, - ReactServerContext, - ServerContextJSONValue, } from 'shared/ReactTypes'; import ReactCurrentDispatcher from './ReactCurrentDispatcher'; diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index a45c56afe7d..a561fbb92e7 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -49,7 +49,6 @@ export function createServerContext( // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. _threadCount: 0, - _definitionLoaded: false, // These are circular Provider: (null: any), Consumer: (null: any), diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index a3fa804df3e..f55fb8d921b 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -73,6 +73,10 @@ export type ReactContext = { // This value may be added by application code // to improve DEV tooling display names displayName?: string, + + // only used by ServerContext + _defaultValue: T, + _globalName: string, ... }; @@ -84,12 +88,7 @@ export type ServerContextJSONValue = | $ReadOnlyArray | {+[key: string]: ServerContextJSONValue}; -export type ReactServerContext = ReactContext & { - _defaultValue: T, - _definitionLoaded: boolean, - _globalName: string, - ... -}; +export type ReactServerContext = ReactContext; export type ReactPortal = { $$typeof: Symbol | number, diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 88444024122..0a86232c7b5 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -66,7 +66,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; -export const enableServerContext = true; +export const enableServerContext = false; export const enableUseMutableSource = false; export const enableTransitionTracing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index a9ccb8f313c..d47c02949a4 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -66,7 +66,7 @@ export const enablePersistentOffscreenHostContainer = false; export const enableCustomElementPropertySupport = false; export const consoleManagedByDevToolsDuringStrictMode = false; -export const enableServerContext = true; +export const enableServerContext = false; export const enableUseMutableSource = false; export const enableTransitionTracing = false; diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 985b928c892..92d3bfd128c 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -403,17 +403,18 @@ "415": "Error parsing the data. It's probably an error code or network corruption.", "416": "This environment don't support binary chunks.", "417": "React currently only supports piping to one writable stream.", - "418": "An error occurred during hydration. The server HTML was replaced with client content", - "419": "ServerContext: %s already defined", - "420": "Hydration failed because the initial UI does not match what was rendered on the server.", - "421": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", - "422": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", - "423": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", - "424": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", - "425": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", - "426": "ServerContext can only have a value prop and children. Found: %s", - "427": "React elements are not allowed in ServerContext", - "428": "useServerContext expects a context created with React.createServerContext", - "429": "useServerContext is only supported while rendering.", - "430": "ServerContext: %s already defined" + "418": "Hydration failed because the initial UI does not match what was rendered on the server.", + "419": "The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.", + "420": "ServerContext: %s already defined", + "421": "This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.", + "422": "There was an error while hydrating this Suspense boundary. Switched to client rendering.", + "423": "There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.", + "424": "This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.", + "425": "Text content does not match server-rendered HTML.", + "426": "A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition.", + "427": "useServerContext expects a context created with React.createServerContext", + "428": "useServerContext is only supported while rendering.", + "429": "ServerContext: %s already defined", + "430": "ServerContext can only have a value prop and children. Found: %s", + "431": "React elements are not allowed in ServerContext" } From 06b6008aec08b7072dbf2cb5d9c4e61073149d50 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 16:01:15 -0500 Subject: [PATCH 61/83] emit chunk for provider --- .../react-client/src/ReactFlightClient.js | 69 ++++++++++++++++--- .../src/ReactFlightClientStream.js | 5 ++ .../ReactFlightDOMRelayServerHostConfig.js | 8 +++ .../ReactFlightNativeRelayServerHostConfig.js | 8 +++ .../react-server/src/ReactFlightServer.js | 42 +++++++++-- .../src/ReactFlightServerConfigStream.js | 9 +++ 6 files changed, 126 insertions(+), 15 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 365565067f5..e90a91d443b 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -28,6 +28,7 @@ import { REACT_LAZY_TYPE, REACT_ELEMENT_TYPE, REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, } from 'shared/ReactSymbols'; import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry'; @@ -42,9 +43,10 @@ export type JSONValue = const PENDING = 0; const RESOLVED_MODEL = 1; -const RESOLVED_MODULE = 2; -const INITIALIZED = 3; -const ERRORED = 4; +const RESOLVED_PROVIDER = 2; +const RESOLVED_MODULE = 3; +const INITIALIZED = 4; +const ERRORED = 5; type PendingChunk = { _status: 0, @@ -58,20 +60,26 @@ type ResolvedModelChunk = { _response: Response, then(resolve: () => mixed): void, }; -type ResolvedModuleChunk = { +type ResolvedProviderChunk = { _status: 2, + _value: string, + _response: Response, + then(resolve: () => mixed): void, +}; +type ResolvedModuleChunk = { + _status: 3, _value: ModuleReference, _response: Response, then(resolve: () => mixed): void, }; type InitializedChunk = { - _status: 3, + _status: 4, _value: T, _response: Response, then(resolve: () => mixed): void, }; type ErroredChunk = { - _status: 4, + _status: 5, _value: Error, _response: Response, then(resolve: () => mixed): void, @@ -114,6 +122,8 @@ function readChunk(chunk: SomeChunk): T { return chunk._value; case RESOLVED_MODEL: return initializeModelChunk(chunk); + case RESOLVED_PROVIDER: + return initializeProviderChunk(chunk); case RESOLVED_MODULE: return initializeModuleChunk(chunk); case PENDING: @@ -173,6 +183,13 @@ function createResolvedModelChunk( return new Chunk(RESOLVED_MODEL, value, response); } +function createResolvedProviderChunk( + response: Response, + value: string, +): ResolvedProviderChunk { + return new Chunk(RESOLVED_PROVIDER, value, response); +} + function createResolvedModuleChunk( response: Response, value: ModuleReference, @@ -195,6 +212,18 @@ function resolveModelChunk( wakeChunk(listeners); } +function resolveProviderChunk(chunk: SomeChunk, value: string): void { + if (chunk._status !== PENDING) { + // We already resolved. We didn't expect to see this. + return; + } + const listeners = chunk._value; + const resolvedChunk: ResolvedProviderChunk = (chunk: any); + resolvedChunk._status = RESOLVED_PROVIDER; + resolvedChunk._value = value; + wakeChunk(listeners); +} + function resolveModuleChunk( chunk: SomeChunk, value: ModuleReference, @@ -218,6 +247,14 @@ function initializeModelChunk(chunk: ResolvedModelChunk): T { return value; } +function initializeProviderChunk(chunk: ResolvedProviderChunk): T { + const value: T = getOrCreateServerContext(chunk._value).Provider; + const initializedChunk: InitializedChunk = (chunk: any); + initializedChunk._status = INITIALIZED; + initializedChunk._value = value; + return value; +} + function initializeModuleChunk(chunk: ResolvedModuleChunk): T { const value: T = requireModule(chunk._value); const initializedChunk: InitializedChunk = (chunk: any); @@ -340,11 +377,7 @@ export function parseModelTuple( // Or even change the ReactElement type to be an array. return createElement(tuple[1], tuple[2], tuple[3]); case REACT_PROVIDER_TYPE: - return createElement( - getOrCreateServerContext((tuple[1]: any)).Provider, - tuple[2], - tuple[3], - ); + return getOrCreateServerContext((tuple[1]: any)).Provider; } return value; } @@ -372,6 +405,20 @@ export function resolveModel( } } +export function resolveProvider( + response: Response, + id: number, + contextName: string, +): void { + const chunks = response._chunks; + const chunk = chunks.get(id); + if (!chunk) { + chunks.set(id, createResolvedProviderChunk(response, contextName)); + } else { + resolveProviderChunk(chunk, contextName); + } +} + export function resolveModule( response: Response, id: number, diff --git a/packages/react-client/src/ReactFlightClientStream.js b/packages/react-client/src/ReactFlightClientStream.js index 9f07d8cc999..8af1734de6b 100644 --- a/packages/react-client/src/ReactFlightClientStream.js +++ b/packages/react-client/src/ReactFlightClientStream.js @@ -12,6 +12,7 @@ import type {Response} from './ReactFlightClientHostConfigStream'; import { resolveModule, resolveModel, + resolveProvider, resolveSymbol, resolveError, createResponse as createResponseBase, @@ -49,6 +50,10 @@ function processFullRow(response: Response, row: string): void { resolveModule(response, id, text); return; } + case 'P': { + resolveProvider(response, id, text); + return; + } case 'S': { resolveSymbol(response, id, JSON.parse(text)); return; diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js index 971eca0908c..a29a6449d8b 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js @@ -125,6 +125,14 @@ export function processModuleChunk( return ['M', id, moduleMetaData]; } +export function processProviderChunk( + request: Request, + id: number, + contextName: string, +): Chunk { + return ['P', id, contextName]; +} + export function processSymbolChunk( request: Request, id: number, diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js index 0387d94ecad..b07c084eaa6 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js @@ -122,6 +122,14 @@ export function processModuleChunk( return ['M', id, moduleMetaData]; } +export function processProviderChunk( + request: Request, + id: number, + contextName: string, +): Chunk { + return ['P', id, contextName]; +} + export function processSymbolChunk( request: Request, id: number, diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 0854ae1b402..15f87ebefe0 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -16,7 +16,10 @@ import type { ModuleKey, } from './ReactFlightServerConfig'; import type {ContextSnapshot} from './ReactFlightNewContext'; -import type {ServerContextJSONValue} from 'shared/ReactTypes'; +import type { + ReactProviderType, + ServerContextJSONValue, +} from 'shared/ReactTypes'; import { scheduleWork, @@ -28,6 +31,7 @@ import { closeWithError, processModelChunk, processModuleChunk, + processProviderChunk, processSymbolChunk, processErrorChunk, resolveModuleMetaData, @@ -51,6 +55,7 @@ import { REACT_LAZY_TYPE, REACT_MEMO_TYPE, REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, REACT_SERVER_CONTEXT_TYPE, } from 'shared/ReactSymbols'; @@ -98,6 +103,7 @@ export type Request = { completedErrorChunks: Array, writtenSymbols: Map, writtenModules: Map, + writtenProviders: Map, onError: (error: mixed) => void, toJSON: (key: string, value: ReactModel) => ReactJSONValue, }; @@ -138,6 +144,7 @@ export function createRequest( completedErrorChunks: [], writtenSymbols: new Map(), writtenModules: new Map(), + writtenProviders: new Map(), onError: onError === undefined ? defaultErrorHandler : onError, toJSON: function(key: string, value: ReactModel): ReactJSONValue { return resolveModelToJSON(request, this, key, value); @@ -217,8 +224,8 @@ function attemptResolveElement( } } return [ - REACT_PROVIDER_TYPE, - type._context._globalName, + REACT_ELEMENT_TYPE, + type, key, // Rely on __popProvider being serialized last to pop the provider. {value: props.value, children: props.children, __pop: type._context}, @@ -452,7 +459,12 @@ export function resolveModelToJSON( } if (__DEV__) { - if (parent[0] === REACT_PROVIDER_TYPE && key === '3') { + if ( + parent[0] === REACT_ELEMENT_TYPE && + parent[1] && + parent[1].$$typeof === REACT_PROVIDER_TYPE && + key === '3' + ) { insideContextProps = value; } else if (insideContextProps === parent && key === 'value') { isInsideContextValue = true; @@ -520,6 +532,19 @@ export function resolveModelToJSON( return (undefined: any); } + if (value.$$typeof === REACT_PROVIDER_TYPE) { + const key = ((value: any): ReactProviderType)._context._globalName; + const writtenProviders = request.writtenProviders; + let providerId = writtenProviders.get(key); + if (providerId === undefined) { + request.pendingChunks++; + providerId = request.nextChunkId++; + writtenProviders.set(key, providerId); + emitProviderChunk(request, providerId, key); + } + return serializeByValueID(providerId); + } + if (typeof value === 'object') { if (isModuleReference(value)) { const moduleReference: ModuleReference = (value: any); @@ -741,6 +766,15 @@ function emitSymbolChunk(request: Request, id: number, name: string): void { request.completedModuleChunks.push(processedChunk); } +function emitProviderChunk( + request: Request, + id: number, + contextName: string, +): void { + const processedChunk = processProviderChunk(request, id, contextName); + request.completedJSONChunks.push(processedChunk); +} + function retrySegment(request: Request, segment: Segment): void { switchContext(segment.context); try { diff --git a/packages/react-server/src/ReactFlightServerConfigStream.js b/packages/react-server/src/ReactFlightServerConfigStream.js index 74a90f7a02a..08e9cbff2f5 100644 --- a/packages/react-server/src/ReactFlightServerConfigStream.js +++ b/packages/react-server/src/ReactFlightServerConfigStream.js @@ -109,6 +109,15 @@ export function processModuleChunk( return stringToChunk(row); } +export function processProviderChunk( + request: Request, + id: number, + contextName: string, +): Chunk { + const row = serializeRowHeader('P', id) + contextName + '\n'; + return stringToChunk(row); +} + export function processSymbolChunk( request: Request, id: number, From c83bc6697ac9ee495d3d1aa51790a20b9670a718 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 16:03:22 -0500 Subject: [PATCH 62/83] remove case for React provider type --- packages/react-client/src/ReactFlightClient.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index e90a91d443b..3c431a86012 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -376,8 +376,6 @@ export function parseModelTuple( // TODO: Consider having React just directly accept these arrays as elements. // Or even change the ReactElement type to be an array. return createElement(tuple[1], tuple[2], tuple[3]); - case REACT_PROVIDER_TYPE: - return getOrCreateServerContext((tuple[1]: any)).Provider; } return value; } From 534d370dfe7106445ccf9692bb33f8e57fec8d1b Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 16:12:28 -0500 Subject: [PATCH 63/83] update type for SomeChunk --- packages/react-client/src/ReactFlightClient.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 3c431a86012..1bb3298e955 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -87,6 +87,7 @@ type ErroredChunk = { type SomeChunk = | PendingChunk | ResolvedModelChunk + | ResolvedProviderChunk | ResolvedModuleChunk | InitializedChunk | ErroredChunk; From 8283b4b733710f4771ecd650922ee5e423972bc0 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 16:14:04 -0500 Subject: [PATCH 64/83] enable flag with experimental --- packages/shared/ReactFeatureFlags.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index ff488467f04..c858be1e506 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -247,7 +247,7 @@ export const enableUpdaterTracking = __PROFILE__; // Only enabled in RN, related to enableComponentStackLocations export const disableNativeComponentFrames = false; -export const enableServerContext = false; +export const enableServerContext = __EXPERIMENTAL__; // Internal only. export const enableGetInspectorDataForInstanceInProduction = false; From c596825d97ef1ecc0cbca434be354477023d0cfb Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 16:20:42 -0500 Subject: [PATCH 65/83] add missing types --- packages/react-client/src/ReactFlightClient.js | 7 +------ .../src/ReactFlightDOMRelayProtocol.js | 1 + packages/react-server/src/ReactFlightServer.js | 8 ++++---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 1bb3298e955..a1cd5af0ce8 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -24,12 +24,7 @@ import { parseModel, } from './ReactFlightClientHostConfig'; -import { - REACT_LAZY_TYPE, - REACT_ELEMENT_TYPE, - REACT_PROVIDER_TYPE, - REACT_SERVER_CONTEXT_TYPE, -} from 'shared/ReactSymbols'; +import {REACT_LAZY_TYPE, REACT_ELEMENT_TYPE} from 'shared/ReactSymbols'; import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry'; diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js index 94f73b0d7a4..31bc2b95f77 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js @@ -20,6 +20,7 @@ export type JSONValue = export type RowEncoding = | ['J', number, JSONValue] | ['M', number, ModuleMetaData] + | ['P', Number, string] | ['S', number, string] | [ 'E', diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 15f87ebefe0..29c6be57fa5 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -55,7 +55,6 @@ import { REACT_LAZY_TYPE, REACT_MEMO_TYPE, REACT_PROVIDER_TYPE, - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, REACT_SERVER_CONTEXT_TYPE, } from 'shared/ReactSymbols'; @@ -533,14 +532,15 @@ export function resolveModelToJSON( } if (value.$$typeof === REACT_PROVIDER_TYPE) { - const key = ((value: any): ReactProviderType)._context._globalName; + const providerKey = ((value: any): ReactProviderType)._context + ._globalName; const writtenProviders = request.writtenProviders; let providerId = writtenProviders.get(key); if (providerId === undefined) { request.pendingChunks++; providerId = request.nextChunkId++; - writtenProviders.set(key, providerId); - emitProviderChunk(request, providerId, key); + writtenProviders.set(providerKey, providerId); + emitProviderChunk(request, providerId, providerKey); } return serializeByValueID(providerId); } From 04293a0097aae37bcaad48b298449331b0965ebd Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 17:02:55 -0500 Subject: [PATCH 66/83] fix flow type --- .../react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js index 31bc2b95f77..60d19ecef3f 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js @@ -20,7 +20,7 @@ export type JSONValue = export type RowEncoding = | ['J', number, JSONValue] | ['M', number, ModuleMetaData] - | ['P', Number, string] + | ['P', number, string] | ['S', number, string] | [ 'E', From 4b780b57dc152b567d7b21ea503e0ff18d1b5b08 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Thu, 3 Mar 2022 23:00:58 -0500 Subject: [PATCH 67/83] missing type --- .../src/ReactFlightNativeRelayProtocol.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayProtocol.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayProtocol.js index 75f6db8039a..1c32ac0dd4d 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayProtocol.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayProtocol.js @@ -20,6 +20,7 @@ export type JSONValue = export type RowEncoding = | ['J', number, JSONValue] | ['M', number, ModuleMetaData] + | ['P', number, string] | ['S', number, string] | [ 'E', From dc4081db8871c50ddc7f665c9faac77ec89ad467 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Fri, 4 Mar 2022 13:36:12 -0500 Subject: [PATCH 68/83] t: any --- packages/react-debug-tools/src/ReactDebugHooks.js | 2 +- .../react-devtools-shared/src/backend/types.js | 2 +- .../react-dom/src/server/ReactPartialRenderer.js | 2 +- .../src/server/ReactPartialRendererHooks.js | 2 +- .../src/ReactNoopFlightServer.js | 8 +------- .../react-reconciler/src/ReactFiberHooks.new.js | 14 +++++++------- .../react-reconciler/src/ReactFiberHooks.old.js | 14 +++++++------- .../src/ReactFiberNewContext.new.js | 10 +++++----- .../src/ReactFiberNewContext.old.js | 10 +++++----- .../react-reconciler/src/ReactFiberScope.new.js | 6 +++--- .../react-reconciler/src/ReactFiberScope.old.js | 6 +++--- .../react-reconciler/src/ReactInternalTypes.js | 2 +- packages/react-server/src/ReactFizzHooks.js | 2 +- packages/react-server/src/ReactFizzNewContext.js | 8 ++++---- packages/shared/ReactTypes.js | 8 +------- 15 files changed, 42 insertions(+), 54 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 74f874ad292..b7159859060 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -105,7 +105,7 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext(context: ReactContext): T { +function readContext(context: ReactContext): T { // For now we don't expose readContext usage in the hooks debugging info. return context._currentValue; } diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index 96261a03bfd..4d975dbfec0 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -83,7 +83,7 @@ export type GetFiberIDForNative = ( ) => number | null; export type FindNativeNodesForFiberID = (id: number) => ?Array; -export type ReactProviderType = { +export type ReactProviderType = { $$typeof: Symbol | number, _context: ReactContext, ... diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 3ac5eb0d5d4..41c28856804 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -833,7 +833,7 @@ class ReactDOMServerRenderer { * https://github.com/facebook/react/pull/12985#issuecomment-396301248 */ - pushProvider(provider: ReactProvider): void { + pushProvider(provider: ReactProvider): void { const index = ++this.contextIndex; const context: ReactContext = provider.type._context; const threadID = this.threadID; diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index eb1eb5c3dca..2940c47bde4 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -222,7 +222,7 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext(context: ReactContext): T { +function readContext(context: ReactContext): T { const threadID = currentPartialRenderer.threadID; validateContextBounds(context, threadID); if (__DEV__) { diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index 32d49b105f4..e416c73b938 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -19,6 +19,7 @@ import type {ReactModel} from 'react-server/src/ReactFlightServer'; import {saveModule} from 'react-noop-renderer/flight-modules'; import ReactFlightServer from 'react-server/flight'; +import {ServerContextJSONValue} from 'shared/ReactTypes'; type Destination = Array; @@ -58,13 +59,6 @@ const ReactNoopFlightServer = ReactFlightServer({ }, }); -type ServerContextJSONValue = - | string - | boolean - | number - | null - | $ReadOnlyArray; - type Options = { onError?: (error: mixed) => void, }; diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index ebc35122d3b..d1c50b42d8c 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -2535,7 +2535,7 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2683,7 +2683,7 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2825,7 +2825,7 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2967,7 +2967,7 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { return readContext(context); }, @@ -3110,7 +3110,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3269,7 +3269,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3428,7 +3428,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 0da9f32eaac..137ee33cba5 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -2535,7 +2535,7 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2683,7 +2683,7 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2825,7 +2825,7 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2967,7 +2967,7 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { return readContext(context); }, @@ -3110,7 +3110,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3269,7 +3269,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, @@ -3428,7 +3428,7 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); return readContext(context); }, diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 350b13f86dd..e5cadd29a9b 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -88,7 +88,7 @@ export function exitDisallowedContextReadInDEV(): void { } } -export function pushProvider( +export function pushProvider( providerFiber: Fiber, context: ReactContext, nextValue: T, @@ -193,7 +193,7 @@ export function scheduleContextWorkOnParentPath( } } -export function propagateContextChange( +export function propagateContextChange( workInProgress: Fiber, context: ReactContext, renderLanes: Lanes, @@ -214,7 +214,7 @@ export function propagateContextChange( } } -function propagateContextChange_eager( +function propagateContextChange_eager( workInProgress: Fiber, context: ReactContext, renderLanes: Lanes, @@ -354,7 +354,7 @@ function propagateContextChange_eager( } } -function propagateContextChanges( +function propagateContextChanges( workInProgress: Fiber, contexts: Array, renderLanes: Lanes, @@ -653,7 +653,7 @@ export function prepareToReadContext( } } -export function readContext(context: ReactContext): T { +export function readContext(context: ReactContext): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index c61e516ac94..2947ddf5ff4 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -88,7 +88,7 @@ export function exitDisallowedContextReadInDEV(): void { } } -export function pushProvider( +export function pushProvider( providerFiber: Fiber, context: ReactContext, nextValue: T, @@ -193,7 +193,7 @@ export function scheduleContextWorkOnParentPath( } } -export function propagateContextChange( +export function propagateContextChange( workInProgress: Fiber, context: ReactContext, renderLanes: Lanes, @@ -214,7 +214,7 @@ export function propagateContextChange( } } -function propagateContextChange_eager( +function propagateContextChange_eager( workInProgress: Fiber, context: ReactContext, renderLanes: Lanes, @@ -354,7 +354,7 @@ function propagateContextChange_eager( } } -function propagateContextChanges( +function propagateContextChanges( workInProgress: Fiber, contexts: Array, renderLanes: Lanes, @@ -653,7 +653,7 @@ export function prepareToReadContext( } } -export function readContext(context: ReactContext): T { +export function readContext(context: ReactContext): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. diff --git a/packages/react-reconciler/src/ReactFiberScope.new.js b/packages/react-reconciler/src/ReactFiberScope.new.js index 9fe9d09572a..ccc589a9dfe 100644 --- a/packages/react-reconciler/src/ReactFiberScope.new.js +++ b/packages/react-reconciler/src/ReactFiberScope.new.js @@ -108,7 +108,7 @@ function collectFirstScopedNodeFromChildren( return null; } -function collectNearestContextValues( +function collectNearestContextValues( node: Fiber, context: ReactContext, childContextValues: Array, @@ -128,7 +128,7 @@ function collectNearestContextValues( } } -function collectNearestChildContextValues( +function collectNearestChildContextValues( startingChild: Fiber | null, context: ReactContext, childContextValues: Array, @@ -176,7 +176,7 @@ function containsNode(node: Object): boolean { return false; } -function getChildContextValues(context: ReactContext): Array { +function getChildContextValues(context: ReactContext): Array { const currentFiber = getInstanceFromScope(this); if (currentFiber === null) { return []; diff --git a/packages/react-reconciler/src/ReactFiberScope.old.js b/packages/react-reconciler/src/ReactFiberScope.old.js index 9fe9d09572a..ccc589a9dfe 100644 --- a/packages/react-reconciler/src/ReactFiberScope.old.js +++ b/packages/react-reconciler/src/ReactFiberScope.old.js @@ -108,7 +108,7 @@ function collectFirstScopedNodeFromChildren( return null; } -function collectNearestContextValues( +function collectNearestContextValues( node: Fiber, context: ReactContext, childContextValues: Array, @@ -128,7 +128,7 @@ function collectNearestContextValues( } } -function collectNearestChildContextValues( +function collectNearestChildContextValues( startingChild: Fiber | null, context: ReactContext, childContextValues: Array, @@ -176,7 +176,7 @@ function containsNode(node: Object): boolean { return false; } -function getChildContextValues(context: ReactContext): Array { +function getChildContextValues(context: ReactContext): Array { const currentFiber = getInstanceFromScope(this); if (currentFiber === null) { return []; diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 5e6096c6348..52ccb02bad1 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -48,7 +48,7 @@ export type HookType = | 'useId' | 'useCacheRefresh'; -export type ContextDependency = { +export type ContextDependency = { context: ReactContext, next: ContextDependency | null, memoizedValue: T, diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index ab740b172f1..c3ffa8cd6ab 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -243,7 +243,7 @@ function getCacheForType(resourceType: () => T): T { throw new Error('Not implemented.'); } -function readContext(context: ReactContext): T { +function readContext(context: ReactContext): T { if (__DEV__) { if (isInHookUserCodeInDev) { console.error( diff --git a/packages/react-server/src/ReactFizzNewContext.js b/packages/react-server/src/ReactFizzNewContext.js index f65a065367f..dfbb1b7749e 100644 --- a/packages/react-server/src/ReactFizzNewContext.js +++ b/packages/react-server/src/ReactFizzNewContext.js @@ -20,7 +20,7 @@ if (__DEV__) { // Used to store the parent path of all context overrides in a shared linked list. // Forming a reverse tree. -type ContextNode = { +type ContextNode = { parent: null | ContextNode, depth: number, // Short hand to compute the depth of the tree at this node. context: ReactContext, @@ -178,7 +178,7 @@ export function switchContext(newSnapshot: ContextSnapshot): void { } } -export function pushProvider( +export function pushProvider( context: ReactContext, nextValue: T, ): ContextSnapshot { @@ -228,7 +228,7 @@ export function pushProvider( return newNode; } -export function popProvider(context: ReactContext): ContextSnapshot { +export function popProvider(context: ReactContext): ContextSnapshot { const prevSnapshot = currentActiveSnapshot; if (prevSnapshot === null) { @@ -296,7 +296,7 @@ export function getActiveContext(): ContextSnapshot { return currentActiveSnapshot; } -export function readContext(context: ReactContext): T { +export function readContext(context: ReactContext): T { const value = isPrimaryRenderer ? context._currentValue : context._currentValue2; diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index f55fb8d921b..17aa509e89e 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -42,12 +42,6 @@ export type ReactProviderType = { ... }; -export type ReactServerProviderType = { - $$typeof: Symbol | number, - _context: ReactServerContext, - ... -}; - export type ReactConsumer = { $$typeof: Symbol | number, type: ReactContext, @@ -88,7 +82,7 @@ export type ServerContextJSONValue = | $ReadOnlyArray | {+[key: string]: ServerContextJSONValue}; -export type ReactServerContext = ReactContext; +export type ReactServerContext = ReactContext; export type ReactPortal = { $$typeof: Symbol | number, From ff6269f73f79df3233847ead38304bfc5f48b9c5 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Fri, 4 Mar 2022 13:42:12 -0500 Subject: [PATCH 69/83] revert extraneous type change --- packages/react-reconciler/src/ReactFiberCacheComponent.new.js | 4 ++-- packages/react-reconciler/src/ReactFiberCacheComponent.old.js | 4 ++-- packages/react-reconciler/src/ReactInternalTypes.js | 4 ++-- packages/react/src/ReactContext.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberCacheComponent.new.js b/packages/react-reconciler/src/ReactFiberCacheComponent.new.js index 3c6c851218a..67588c3219a 100644 --- a/packages/react-reconciler/src/ReactFiberCacheComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberCacheComponent.new.js @@ -48,8 +48,8 @@ export const CacheContext: ReactContext = enableCache _currentValue: (null: any), _currentValue2: (null: any), _threadCount: 0, - _defaultValue: (undefined: any), - _globalName: (undefined: any), + _defaultValue: (null: any), + _globalName: (null: any), } : (null: any); diff --git a/packages/react-reconciler/src/ReactFiberCacheComponent.old.js b/packages/react-reconciler/src/ReactFiberCacheComponent.old.js index dcc83785117..e530619d416 100644 --- a/packages/react-reconciler/src/ReactFiberCacheComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberCacheComponent.old.js @@ -48,8 +48,8 @@ export const CacheContext: ReactContext = enableCache _currentValue: (null: any), _currentValue2: (null: any), _threadCount: 0, - _defaultValue: (undefined: any), - _globalName: (undefined: any), + _defaultValue: (null: any), + _globalName: (null: any), } : (null: any); diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 52ccb02bad1..e9d42c20e02 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -50,14 +50,14 @@ export type HookType = export type ContextDependency = { context: ReactContext, - next: ContextDependency | null, + next: ContextDependency | null, memoizedValue: T, ... }; export type Dependencies = { lanes: Lanes, - firstContext: ContextDependency | null, + firstContext: ContextDependency | null, ... }; diff --git a/packages/react/src/ReactContext.js b/packages/react/src/ReactContext.js index 374d4d3126d..e547a411009 100644 --- a/packages/react/src/ReactContext.js +++ b/packages/react/src/ReactContext.js @@ -32,8 +32,8 @@ export function createContext(defaultValue: T): ReactContext { Consumer: (null: any), // Add these to use same hidden class in VM as ServerContext - _defaultValue: (undefined: any), - _globalName: (undefined: any), + _defaultValue: (null: any), + _globalName: (null: any), }; context.Provider = { From f5a8b2580b9ddbc0a5cf47789af8ee1779c64e09 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Fri, 4 Mar 2022 13:56:17 -0500 Subject: [PATCH 70/83] better type --- packages/react-reconciler/src/ReactInternalTypes.js | 2 +- packages/react-server/src/ReactFlightHooks.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index e9d42c20e02..dd2e09c03b2 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -342,7 +342,7 @@ type Dispatch = A => void; export type Dispatcher = {| getCacheSignal?: () => AbortSignal, getCacheForType?: (resourceType: () => T) => T, - readContext(context: ReactContext): T, + readContext(context: ReactContext): T, useState(initialState: (() => S) | S): [S, Dispatch>], useReducer( reducer: (S, A) => S, diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index f8c5c833cab..66c31536593 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -56,7 +56,7 @@ export const Dispatcher: DispatcherType = { } return entry; }, - readContext, + readContext: (readContext: any), useContext: (readContext: any), useReducer: (unsupportedHook: any), useRef: (unsupportedHook: any), From a714680d6270a911cc235807b38f59187d7ad9ec Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Fri, 4 Mar 2022 13:59:03 -0500 Subject: [PATCH 71/83] better type --- packages/react-server/src/ReactFlightHooks.js | 8 ++++---- packages/react-server/src/ReactFlightNewContext.js | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index 66c31536593..ac63b774ddc 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -16,7 +16,7 @@ import type { import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; import {readContext as readContextImpl} from './ReactFlightNewContext'; -function readContext(context: ReactContext): T { +function readContext(context: ReactServerContext): T { if (__DEV__) { if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { console.error('Only ServerContext is supported in Flight'); @@ -30,7 +30,7 @@ function readContext(context: ReactContext): T { ); } } - return readContextImpl(((context: any): ReactServerContext)); + return readContextImpl(context); } export const Dispatcher: DispatcherType = { @@ -56,8 +56,8 @@ export const Dispatcher: DispatcherType = { } return entry; }, - readContext: (readContext: any), - useContext: (readContext: any), + readContext, + useContext: readContext, useReducer: (unsupportedHook: any), useRef: (unsupportedHook: any), useState: (unsupportedHook: any), diff --git a/packages/react-server/src/ReactFlightNewContext.js b/packages/react-server/src/ReactFlightNewContext.js index b3c52c71157..7d2547e74b4 100644 --- a/packages/react-server/src/ReactFlightNewContext.js +++ b/packages/react-server/src/ReactFlightNewContext.js @@ -239,8 +239,6 @@ export function getActiveContext(): ContextSnapshot { return currentActiveSnapshot; } -export function readContext( - context: ReactServerContext, -): T { +export function readContext(context: ReactServerContext): T { return context._currentValue; } From 81f798f7189ca72c3ca81046f06ff887faf627b9 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Mar 2022 14:25:27 -0500 Subject: [PATCH 72/83] feedback --- .../react-client/src/ReactFlightClient.js | 71 +++-------- .../react-server/src/ReactFizzNewContext.js | 2 +- packages/react-server/src/ReactFlightHooks.js | 6 +- .../react-server/src/ReactFlightNewContext.js | 120 ++++++++++++++---- packages/shared/ReactSymbols.js | 2 +- 5 files changed, 112 insertions(+), 89 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index a1cd5af0ce8..dabb215c708 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -38,10 +38,9 @@ export type JSONValue = const PENDING = 0; const RESOLVED_MODEL = 1; -const RESOLVED_PROVIDER = 2; -const RESOLVED_MODULE = 3; -const INITIALIZED = 4; -const ERRORED = 5; +const RESOLVED_MODULE = 2; +const INITIALIZED = 3; +const ERRORED = 4; type PendingChunk = { _status: 0, @@ -55,26 +54,20 @@ type ResolvedModelChunk = { _response: Response, then(resolve: () => mixed): void, }; -type ResolvedProviderChunk = { - _status: 2, - _value: string, - _response: Response, - then(resolve: () => mixed): void, -}; type ResolvedModuleChunk = { - _status: 3, + _status: 2, _value: ModuleReference, _response: Response, then(resolve: () => mixed): void, }; type InitializedChunk = { - _status: 4, + _status: 3, _value: T, _response: Response, then(resolve: () => mixed): void, }; type ErroredChunk = { - _status: 5, + _status: 4, _value: Error, _response: Response, then(resolve: () => mixed): void, @@ -82,7 +75,6 @@ type ErroredChunk = { type SomeChunk = | PendingChunk | ResolvedModelChunk - | ResolvedProviderChunk | ResolvedModuleChunk | InitializedChunk | ErroredChunk; @@ -118,8 +110,6 @@ function readChunk(chunk: SomeChunk): T { return chunk._value; case RESOLVED_MODEL: return initializeModelChunk(chunk); - case RESOLVED_PROVIDER: - return initializeProviderChunk(chunk); case RESOLVED_MODULE: return initializeModuleChunk(chunk); case PENDING: @@ -179,13 +169,6 @@ function createResolvedModelChunk( return new Chunk(RESOLVED_MODEL, value, response); } -function createResolvedProviderChunk( - response: Response, - value: string, -): ResolvedProviderChunk { - return new Chunk(RESOLVED_PROVIDER, value, response); -} - function createResolvedModuleChunk( response: Response, value: ModuleReference, @@ -208,18 +191,6 @@ function resolveModelChunk( wakeChunk(listeners); } -function resolveProviderChunk(chunk: SomeChunk, value: string): void { - if (chunk._status !== PENDING) { - // We already resolved. We didn't expect to see this. - return; - } - const listeners = chunk._value; - const resolvedChunk: ResolvedProviderChunk = (chunk: any); - resolvedChunk._status = RESOLVED_PROVIDER; - resolvedChunk._value = value; - wakeChunk(listeners); -} - function resolveModuleChunk( chunk: SomeChunk, value: ModuleReference, @@ -243,14 +214,6 @@ function initializeModelChunk(chunk: ResolvedModelChunk): T { return value; } -function initializeProviderChunk(chunk: ResolvedProviderChunk): T { - const value: T = getOrCreateServerContext(chunk._value).Provider; - const initializedChunk: InitializedChunk = (chunk: any); - initializedChunk._status = INITIALIZED; - initializedChunk._value = value; - return value; -} - function initializeModuleChunk(chunk: ResolvedModuleChunk): T { const value: T = requireModule(chunk._value); const initializedChunk: InitializedChunk = (chunk: any); @@ -367,11 +330,10 @@ export function parseModelTuple( ): any { const tuple: [mixed, mixed, mixed, mixed] = (value: any); - switch (tuple[0]) { - case REACT_ELEMENT_TYPE: - // TODO: Consider having React just directly accept these arrays as elements. - // Or even change the ReactElement type to be an array. - return createElement(tuple[1], tuple[2], tuple[3]); + if (tuple[0] === REACT_ELEMENT_TYPE) { + // TODO: Consider having React just directly accept these arrays as elements. + // Or even change the ReactElement type to be an array. + return createElement(tuple[1], tuple[2], tuple[3]); } return value; } @@ -405,12 +367,13 @@ export function resolveProvider( contextName: string, ): void { const chunks = response._chunks; - const chunk = chunks.get(id); - if (!chunk) { - chunks.set(id, createResolvedProviderChunk(response, contextName)); - } else { - resolveProviderChunk(chunk, contextName); - } + chunks.set( + id, + createInitializedChunk( + response, + getOrCreateServerContext(contextName).Provider, + ), + ); } export function resolveModule( diff --git a/packages/react-server/src/ReactFizzNewContext.js b/packages/react-server/src/ReactFizzNewContext.js index dfbb1b7749e..4e9f01a7ac0 100644 --- a/packages/react-server/src/ReactFizzNewContext.js +++ b/packages/react-server/src/ReactFizzNewContext.js @@ -7,9 +7,9 @@ * @flow */ -import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; import type {ReactContext, ReactServerContext} from 'shared/ReactTypes'; +import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; import {isPrimaryRenderer} from './ReactServerFormatConfig'; let rendererSigil; diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index ac63b774ddc..88a2eac86ca 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -8,11 +8,7 @@ */ import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes'; -import type { - ReactContext, - ReactServerContext, - ServerContextJSONValue, -} from 'shared/ReactTypes'; +import type {ReactServerContext} from 'shared/ReactTypes'; import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; import {readContext as readContextImpl} from './ReactFlightNewContext'; diff --git a/packages/react-server/src/ReactFlightNewContext.js b/packages/react-server/src/ReactFlightNewContext.js index 7d2547e74b4..e6d57a149ba 100644 --- a/packages/react-server/src/ReactFlightNewContext.js +++ b/packages/react-server/src/ReactFlightNewContext.js @@ -12,6 +12,9 @@ import type { ServerContextJSONValue, } from 'shared/ReactTypes'; +import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; +import {isPrimaryRenderer} from './ReactServerFormatConfig'; + let rendererSigil; if (__DEV__) { // Use this to detect multiple renderers using the same context @@ -40,11 +43,19 @@ export const rootContextSnapshot: ContextSnapshot = null; let currentActiveSnapshot: ContextSnapshot = null; function popNode(prev: ContextNode): void { - prev.context._currentValue = prev.parentValue; + if (isPrimaryRenderer) { + prev.context._currentValue = prev.parentValue; + } else { + prev.context._currentValue2 = prev.parentValue; + } } function pushNode(next: ContextNode): void { - next.context._currentValue = next.value; + if (isPrimaryRenderer) { + next.context._currentValue = next.value; + } else { + next.context._currentValue2 = next.value; + } } function popToNearestCommonAncestor( @@ -173,20 +184,39 @@ export function pushProvider( context: ReactServerContext, nextValue: T, ): ContextSnapshot { - const prevValue = context._currentValue; - context._currentValue = nextValue; - if (__DEV__) { - if ( - context._currentRenderer !== undefined && - context._currentRenderer !== null && - context._currentRenderer !== rendererSigil - ) { - console.error( - 'Detected multiple renderers concurrently rendering the ' + - 'same context provider. This is currently unsupported.', - ); + let prevValue; + if (isPrimaryRenderer) { + prevValue = context._currentValue; + context._currentValue = nextValue; + if (__DEV__) { + if ( + context._currentRenderer !== undefined && + context._currentRenderer !== null && + context._currentRenderer !== rendererSigil + ) { + console.error( + 'Detected multiple renderers concurrently rendering the ' + + 'same context provider. This is currently unsupported.', + ); + } + context._currentRenderer = rendererSigil; + } + } else { + prevValue = context._currentValue2; + context._currentValue2 = nextValue; + if (__DEV__) { + if ( + context._currentRenderer2 !== undefined && + context._currentRenderer2 !== null && + context._currentRenderer2 !== rendererSigil + ) { + console.error( + 'Detected multiple renderers concurrently rendering the ' + + 'same context provider. This is currently unsupported.', + ); + } + context._currentRenderer2 = rendererSigil; } - context._currentRenderer = rendererSigil; } const prevNode = currentActiveSnapshot; const newNode: ContextNode = { @@ -218,19 +248,50 @@ export function popProvider( ); } } - prevSnapshot.context._currentValue = prevSnapshot.parentValue; - if (__DEV__) { - if ( - context._currentRenderer !== undefined && - context._currentRenderer !== null && - context._currentRenderer !== rendererSigil - ) { - console.error( - 'Detected multiple renderers concurrently rendering the ' + - 'same context provider. This is currently unsupported.', - ); + if (isPrimaryRenderer) { + const value = prevSnapshot.parentValue; + if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + prevSnapshot.context._currentValue = + // $FlowExpectedError - Effectively refined context to ServerContext + (prevSnapshot.context: ReactServerContext)._defaultValue; + } else { + prevSnapshot.context._currentValue = value; + } + if (__DEV__) { + if ( + context._currentRenderer !== undefined && + context._currentRenderer !== null && + context._currentRenderer !== rendererSigil + ) { + console.error( + 'Detected multiple renderers concurrently rendering the ' + + 'same context provider. This is currently unsupported.', + ); + } + context._currentRenderer = rendererSigil; + } + } else { + const value = prevSnapshot.parentValue; + if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + prevSnapshot.context._currentValue2 = + // $FlowExpectedError - Effectively refined context to ServerContext + (prevSnapshot.context: ReactServerContext)._defaultValue; + } else { + prevSnapshot.context._currentValue2 = value; + } + if (__DEV__) { + if ( + context._currentRenderer2 !== undefined && + context._currentRenderer2 !== null && + context._currentRenderer2 !== rendererSigil + ) { + console.error( + 'Detected multiple renderers concurrently rendering the ' + + 'same context provider. This is currently unsupported.', + ); + } + context._currentRenderer2 = rendererSigil; } - context._currentRenderer = rendererSigil; } return (currentActiveSnapshot = prevSnapshot.parent); } @@ -240,5 +301,8 @@ export function getActiveContext(): ContextSnapshot { } export function readContext(context: ReactServerContext): T { - return context._currentValue; + const value = isPrimaryRenderer + ? context._currentValue + : context._currentValue2; + return value; } diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index a9147c9d1d1..6ff9305fa7b 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -34,7 +34,7 @@ export const REACT_LEGACY_HIDDEN_TYPE = Symbol.for('react.legacy_hidden'); export const REACT_CACHE_TYPE = Symbol.for('react.cache'); export const REACT_TRACING_MARKER_TYPE = Symbol.for('react.tracing_marker'); export const REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = Symbol.for( - 'react.server_context.defaultValue', + 'react.default_value', ); const MAYBE_ITERATOR_SYMBOL = Symbol.iterator; From 4b808b105c7ab99d11f0bd5d6945f9cc5ffccb5a Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Mar 2022 16:45:09 -0500 Subject: [PATCH 73/83] change import to type import --- packages/react-noop-renderer/src/ReactNoopFlightServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index e416c73b938..1c607befe74 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -15,11 +15,11 @@ */ import type {ReactModel} from 'react-server/src/ReactFlightServer'; +import type {ServerContextJSONValue} from 'shared/ReactTypes'; import {saveModule} from 'react-noop-renderer/flight-modules'; import ReactFlightServer from 'react-server/flight'; -import {ServerContextJSONValue} from 'shared/ReactTypes'; type Destination = Array; From 463047c764b2907c85bd6ac91dfa94fc00cd0747 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Mar 2022 17:14:36 -0500 Subject: [PATCH 74/83] test? --- .../src/ReactFiberNewContext.new.js | 10 +++------- .../src/ReactFiberNewContext.old.js | 10 +++------- packages/react-server/src/ReactFizzNewContext.js | 14 ++++++-------- packages/react-server/src/ReactFlightNewContext.js | 8 ++------ packages/react/index.stable.js | 1 - 5 files changed, 14 insertions(+), 29 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index e5cadd29a9b..6dffd8c8c4a 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactContext, - ReactServerContext, - ReactProviderType, -} from 'shared/ReactTypes'; +import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; import type { Fiber, ContextDependency, @@ -138,13 +134,13 @@ export function popProvider( pop(valueCursor, providerFiber); if (isPrimaryRenderer) { if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - context._currentValue = ((context: any): ReactServerContext)._defaultValue; + context._currentValue = context._defaultValue; } else { context._currentValue = currentValue; } } else { if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - context._currentValue2 = ((context: any): ReactServerContext)._defaultValue; + context._currentValue2 = context._defaultValue; } else { context._currentValue2 = currentValue; } diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 2947ddf5ff4..a67068fceed 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactContext, - ReactServerContext, - ReactProviderType, -} from 'shared/ReactTypes'; +import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; import type { Fiber, ContextDependency, @@ -138,13 +134,13 @@ export function popProvider( pop(valueCursor, providerFiber); if (isPrimaryRenderer) { if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - context._currentValue = ((context: any): ReactServerContext)._defaultValue; + context._currentValue = context._defaultValue; } else { context._currentValue = currentValue; } } else { if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - context._currentValue2 = ((context: any): ReactServerContext)._defaultValue; + context._currentValue2 = context._defaultValue; } else { context._currentValue2 = currentValue; } diff --git a/packages/react-server/src/ReactFizzNewContext.js b/packages/react-server/src/ReactFizzNewContext.js index 4e9f01a7ac0..7931402b04b 100644 --- a/packages/react-server/src/ReactFizzNewContext.js +++ b/packages/react-server/src/ReactFizzNewContext.js @@ -7,10 +7,12 @@ * @flow */ -import type {ReactContext, ReactServerContext} from 'shared/ReactTypes'; +import type {ReactContext} from 'shared/ReactTypes'; import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; -import {isPrimaryRenderer} from './ReactServerFormatConfig'; +// import {isPrimaryRenderer} from './ReactServerFormatConfig'; + +let isPrimaryRenderer = true; let rendererSigil; if (__DEV__) { @@ -247,9 +249,7 @@ export function popProvider(context: ReactContext): ContextSnapshot { if (isPrimaryRenderer) { const value = prevSnapshot.parentValue; if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - prevSnapshot.context._currentValue = - // $FlowExpectedError - Effectively refined context to ServerContext - (prevSnapshot.context: ReactServerContext)._defaultValue; + prevSnapshot.context._currentValue = prevSnapshot.context._defaultValue; } else { prevSnapshot.context._currentValue = value; } @@ -269,9 +269,7 @@ export function popProvider(context: ReactContext): ContextSnapshot { } else { const value = prevSnapshot.parentValue; if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - prevSnapshot.context._currentValue2 = - // $FlowExpectedError - Effectively refined context to ServerContext - (prevSnapshot.context: ReactServerContext)._defaultValue; + prevSnapshot.context._currentValue2 = prevSnapshot.context._defaultValue; } else { prevSnapshot.context._currentValue2 = value; } diff --git a/packages/react-server/src/ReactFlightNewContext.js b/packages/react-server/src/ReactFlightNewContext.js index e6d57a149ba..91cc1875e47 100644 --- a/packages/react-server/src/ReactFlightNewContext.js +++ b/packages/react-server/src/ReactFlightNewContext.js @@ -251,9 +251,7 @@ export function popProvider( if (isPrimaryRenderer) { const value = prevSnapshot.parentValue; if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - prevSnapshot.context._currentValue = - // $FlowExpectedError - Effectively refined context to ServerContext - (prevSnapshot.context: ReactServerContext)._defaultValue; + prevSnapshot.context._currentValue = prevSnapshot.context._defaultValue; } else { prevSnapshot.context._currentValue = value; } @@ -273,9 +271,7 @@ export function popProvider( } else { const value = prevSnapshot.parentValue; if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { - prevSnapshot.context._currentValue2 = - // $FlowExpectedError - Effectively refined context to ServerContext - (prevSnapshot.context: ReactServerContext)._defaultValue; + prevSnapshot.context._currentValue2 = prevSnapshot.context._defaultValue; } else { prevSnapshot.context._currentValue2 = value; } diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js index 0dedf2d8d90..3ed868197b6 100644 --- a/packages/react/index.stable.js +++ b/packages/react/index.stable.js @@ -22,7 +22,6 @@ export { createElement, createFactory, createRef, - createServerContext, forwardRef, isValidElement, lazy, From 3d16208b88337e07c07cb4e25beeeec1bac5b1a1 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Mar 2022 17:24:52 -0500 Subject: [PATCH 75/83] test? --- packages/react-server/src/ReactFizzNewContext.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/react-server/src/ReactFizzNewContext.js b/packages/react-server/src/ReactFizzNewContext.js index 7931402b04b..b4386399c33 100644 --- a/packages/react-server/src/ReactFizzNewContext.js +++ b/packages/react-server/src/ReactFizzNewContext.js @@ -10,9 +10,7 @@ import type {ReactContext} from 'shared/ReactTypes'; import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; -// import {isPrimaryRenderer} from './ReactServerFormatConfig'; - -let isPrimaryRenderer = true; +import {isPrimaryRenderer} from './ReactServerFormatConfig'; let rendererSigil; if (__DEV__) { From 1c4eaf962c6118b3631e79cf9a22961d264cb10c Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Mar 2022 17:37:26 -0500 Subject: [PATCH 76/83] remove react-dom --- packages/react-server-dom-webpack/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-server-dom-webpack/package.json b/packages/react-server-dom-webpack/package.json index ababa89250e..0a8c3389de7 100644 --- a/packages/react-server-dom-webpack/package.json +++ b/packages/react-server-dom-webpack/package.json @@ -48,7 +48,6 @@ }, "peerDependencies": { "react": "^17.0.0", - "react-dom": "^17.0.0", "webpack": "^5.59.0" }, "dependencies": { From af300fc5d0f9672e7b157c9adbf470e77cf118e2 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Mar 2022 17:47:08 -0500 Subject: [PATCH 77/83] remove react-native-renderer from react-server-native-relay/package.json --- packages/react-server-native-relay/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-server-native-relay/package.json b/packages/react-server-native-relay/package.json index 241913f91e6..e97b2f9b2be 100644 --- a/packages/react-server-native-relay/package.json +++ b/packages/react-server-native-relay/package.json @@ -11,7 +11,6 @@ "scheduler": "^0.11.0" }, "peerDependencies": { - "react": "^17.0.0", - "react-native-renderer": "^17.0.0" + "react": "^17.0.0" } } From 9333935c5fe9647cb001b8e73bcbc106c4ff0177 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Mar 2022 23:05:30 -0500 Subject: [PATCH 78/83] gate change in FiberNewContext, getComponentNameFromType, use switch statement in FlightServer --- .../src/ReactFiberNewContext.new.js | 11 +++- .../src/ReactFiberNewContext.old.js | 11 +++- .../react-server/src/ReactFlightServer.js | 52 +++++++++---------- packages/shared/getComponentNameFromType.js | 3 ++ 4 files changed, 46 insertions(+), 31 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 6dffd8c8c4a..8f269a40502 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -44,6 +44,7 @@ import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork.new'; import { enableSuspenseServerRenderer, enableLazyContextPropagation, + enableServerContext, } from 'shared/ReactFeatureFlags'; import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; @@ -133,13 +134,19 @@ export function popProvider( const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + if ( + enableServerContext && + currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { context._currentValue = context._defaultValue; } else { context._currentValue = currentValue; } } else { - if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + if ( + enableServerContext && + currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { context._currentValue2 = context._defaultValue; } else { context._currentValue2 = currentValue; diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index a67068fceed..a48c8420438 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -44,6 +44,7 @@ import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork.old'; import { enableSuspenseServerRenderer, enableLazyContextPropagation, + enableServerContext, } from 'shared/ReactFeatureFlags'; import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; @@ -133,13 +134,19 @@ export function popProvider( const currentValue = valueCursor.current; pop(valueCursor, providerFiber); if (isPrimaryRenderer) { - if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + if ( + enableServerContext && + currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { context._currentValue = context._defaultValue; } else { context._currentValue = currentValue; } } else { - if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + if ( + enableServerContext && + currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED + ) { context._currentValue2 = context._defaultValue; } else { context._currentValue2 = currentValue; diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 29c6be57fa5..1ea07431044 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -518,33 +518,6 @@ export function resolveModelToJSON( return null; } - if ( - value && - value.$$typeof === REACT_SERVER_CONTEXT_TYPE && - key === '__pop' - ) { - popProvider((value: any)); - if (__DEV__) { - insideContextProps = null; - isInsideContextValue = false; - } - return (undefined: any); - } - - if (value.$$typeof === REACT_PROVIDER_TYPE) { - const providerKey = ((value: any): ReactProviderType)._context - ._globalName; - const writtenProviders = request.writtenProviders; - let providerId = writtenProviders.get(key); - if (providerId === undefined) { - request.pendingChunks++; - providerId = request.nextChunkId++; - writtenProviders.set(providerKey, providerId); - emitProviderChunk(request, providerId, providerKey); - } - return serializeByValueID(providerId); - } - if (typeof value === 'object') { if (isModuleReference(value)) { const moduleReference: ModuleReference = (value: any); @@ -587,6 +560,31 @@ export function resolveModelToJSON( return serializeByValueID(errorId); } } + switch ((value: any).$$typeof) { + case REACT_PROVIDER_TYPE: { + const providerKey = ((value: any): ReactProviderType)._context + ._globalName; + const writtenProviders = request.writtenProviders; + let providerId = writtenProviders.get(key); + if (providerId === undefined) { + request.pendingChunks++; + providerId = request.nextChunkId++; + writtenProviders.set(providerKey, providerId); + emitProviderChunk(request, providerId, providerKey); + } + return serializeByValueID(providerId); + } + case REACT_SERVER_CONTEXT_TYPE: { + if (key === '__pop') { + popProvider((value: any)); + if (__DEV__) { + insideContextProps = null; + isInsideContextValue = false; + } + return (undefined: any); + } + } + } if (__DEV__) { if (value !== null && !isArray(value)) { diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 36432e56aca..9c7b4e02927 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -24,6 +24,7 @@ import { REACT_LAZY_TYPE, REACT_CACHE_TYPE, REACT_TRACING_MARKER_TYPE, + REACT_SERVER_CONTEXT_TYPE, } from 'shared/ReactSymbols'; // Keep in sync with react-reconciler/getComponentNameFromFiber @@ -85,6 +86,8 @@ export default function getComponentNameFromType(type: mixed): string | null { } if (typeof type === 'object') { switch (type.$$typeof) { + case REACT_SERVER_CONTEXT_TYPE: + return ((type: any): ReactContext)._globalName + '.Provider'; case REACT_CONTEXT_TYPE: const context: ReactContext = (type: any); return getContextName(context) + '.Consumer'; From 2d5129c8bbe4bbdd96b198d69982309d286c643f Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Mar 2022 23:36:06 -0500 Subject: [PATCH 79/83] getComponentNameFromTpe: server context type gated and use displayName if available --- packages/shared/getComponentNameFromType.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 9c7b4e02927..ef6e938636b 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -27,6 +27,8 @@ import { REACT_SERVER_CONTEXT_TYPE, } from 'shared/ReactSymbols'; +import {enableServerContext} from 'shared/ReactFeatureFlags'; + // Keep in sync with react-reconciler/getComponentNameFromFiber function getWrappedName( outerType: mixed, @@ -86,8 +88,6 @@ export default function getComponentNameFromType(type: mixed): string | null { } if (typeof type === 'object') { switch (type.$$typeof) { - case REACT_SERVER_CONTEXT_TYPE: - return ((type: any): ReactContext)._globalName + '.Provider'; case REACT_CONTEXT_TYPE: const context: ReactContext = (type: any); return getContextName(context) + '.Consumer'; @@ -112,6 +112,12 @@ export default function getComponentNameFromType(type: mixed): string | null { return null; } } + case REACT_SERVER_CONTEXT_TYPE: { + if (enableServerContext) { + const context = ((type: any): ReactContext); + return (context.displayName || context._globalName) + '.Provider'; + } + } } } return null; From 573d3940a1aaff5ca705c2e7135d6f0445850c01 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Mar 2022 23:44:13 -0500 Subject: [PATCH 80/83] fallthrough --- packages/shared/getComponentNameFromType.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index ef6e938636b..f70f5d9921a 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -112,12 +112,12 @@ export default function getComponentNameFromType(type: mixed): string | null { return null; } } - case REACT_SERVER_CONTEXT_TYPE: { + case REACT_SERVER_CONTEXT_TYPE: if (enableServerContext) { const context = ((type: any): ReactContext); return (context.displayName || context._globalName) + '.Provider'; } - } + // eslint-disable-next-line no-fallthrough } } return null; From 26c566703971268d5a9b17ae76ee3c6511b7fffc Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Mon, 7 Mar 2022 23:45:18 -0500 Subject: [PATCH 81/83] lint.... --- packages/shared/getComponentNameFromType.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index f70f5d9921a..937811da6b7 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -114,8 +114,8 @@ export default function getComponentNameFromType(type: mixed): string | null { } case REACT_SERVER_CONTEXT_TYPE: if (enableServerContext) { - const context = ((type: any): ReactContext); - return (context.displayName || context._globalName) + '.Provider'; + const context2 = ((type: any): ReactContext); + return (context2.displayName || context2._globalName) + '.Provider'; } // eslint-disable-next-line no-fallthrough } From 278499d42036107160aaf00ae62b91f911b78dea Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Tue, 8 Mar 2022 07:35:06 -0500 Subject: [PATCH 82/83] POP --- .../react-server/src/ReactFlightNewContext.js | 37 +--------------- .../react-server/src/ReactFlightServer.js | 44 +++++++++---------- 2 files changed, 21 insertions(+), 60 deletions(-) diff --git a/packages/react-server/src/ReactFlightNewContext.js b/packages/react-server/src/ReactFlightNewContext.js index 91cc1875e47..2a61709b10a 100644 --- a/packages/react-server/src/ReactFlightNewContext.js +++ b/packages/react-server/src/ReactFlightNewContext.js @@ -230,9 +230,7 @@ export function pushProvider( return newNode; } -export function popProvider( - context: ReactServerContext, -): ContextSnapshot { +export function popProvider(): ContextSnapshot { const prevSnapshot = currentActiveSnapshot; if (prevSnapshot === null) { @@ -241,13 +239,6 @@ export function popProvider( ); } - if (__DEV__) { - if (prevSnapshot.context !== context) { - console.error( - 'The parent context is not the expected context. This is probably a bug in React.', - ); - } - } if (isPrimaryRenderer) { const value = prevSnapshot.parentValue; if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { @@ -255,19 +246,6 @@ export function popProvider( } else { prevSnapshot.context._currentValue = value; } - if (__DEV__) { - if ( - context._currentRenderer !== undefined && - context._currentRenderer !== null && - context._currentRenderer !== rendererSigil - ) { - console.error( - 'Detected multiple renderers concurrently rendering the ' + - 'same context provider. This is currently unsupported.', - ); - } - context._currentRenderer = rendererSigil; - } } else { const value = prevSnapshot.parentValue; if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { @@ -275,19 +253,6 @@ export function popProvider( } else { prevSnapshot.context._currentValue2 = value; } - if (__DEV__) { - if ( - context._currentRenderer2 !== undefined && - context._currentRenderer2 !== null && - context._currentRenderer2 !== rendererSigil - ) { - console.error( - 'Detected multiple renderers concurrently rendering the ' + - 'same context provider. This is currently unsupported.', - ); - } - context._currentRenderer2 = rendererSigil; - } } return (currentActiveSnapshot = prevSnapshot.parent); } diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 1ea07431044..eea445d5312 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -162,6 +162,8 @@ function createRootContext( return importServerContexts(reqContext); } +const POP = {}; + function attemptResolveElement( type: any, key: null | React$Key, @@ -227,7 +229,7 @@ function attemptResolveElement( type, key, // Rely on __popProvider being serialized last to pop the provider. - {value: props.value, children: props.children, __pop: type._context}, + {value: props.value, children: props.children, __pop: POP}, ]; } } @@ -559,31 +561,25 @@ export function resolveModelToJSON( emitErrorChunk(request, errorId, x); return serializeByValueID(errorId); } - } - switch ((value: any).$$typeof) { - case REACT_PROVIDER_TYPE: { - const providerKey = ((value: any): ReactProviderType)._context - ._globalName; - const writtenProviders = request.writtenProviders; - let providerId = writtenProviders.get(key); - if (providerId === undefined) { - request.pendingChunks++; - providerId = request.nextChunkId++; - writtenProviders.set(providerKey, providerId); - emitProviderChunk(request, providerId, providerKey); - } - return serializeByValueID(providerId); + } else if ((value: any).$$typeof === REACT_PROVIDER_TYPE) { + const providerKey = ((value: any): ReactProviderType)._context + ._globalName; + const writtenProviders = request.writtenProviders; + let providerId = writtenProviders.get(key); + if (providerId === undefined) { + request.pendingChunks++; + providerId = request.nextChunkId++; + writtenProviders.set(providerKey, providerId); + emitProviderChunk(request, providerId, providerKey); } - case REACT_SERVER_CONTEXT_TYPE: { - if (key === '__pop') { - popProvider((value: any)); - if (__DEV__) { - insideContextProps = null; - isInsideContextValue = false; - } - return (undefined: any); - } + return serializeByValueID(providerId); + } else if (value === POP) { + popProvider(); + if (__DEV__) { + insideContextProps = null; + isInsideContextValue = false; } + return (undefined: any); } if (__DEV__) { From b6bbe30d866b847c73382dfd1da3c7e74967c228 Mon Sep 17 00:00:00 2001 From: Marco Salazar Date: Tue, 8 Mar 2022 07:42:31 -0500 Subject: [PATCH 83/83] lint --- packages/react-server/src/ReactFlightNewContext.js | 2 +- packages/react-server/src/ReactFlightServer.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-server/src/ReactFlightNewContext.js b/packages/react-server/src/ReactFlightNewContext.js index 2a61709b10a..3f5abaaf079 100644 --- a/packages/react-server/src/ReactFlightNewContext.js +++ b/packages/react-server/src/ReactFlightNewContext.js @@ -230,7 +230,7 @@ export function pushProvider( return newNode; } -export function popProvider(): ContextSnapshot { +export function popProvider(): ContextSnapshot { const prevSnapshot = currentActiveSnapshot; if (prevSnapshot === null) { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index eea445d5312..32a08b1eff8 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -55,7 +55,6 @@ import { REACT_LAZY_TYPE, REACT_MEMO_TYPE, REACT_PROVIDER_TYPE, - REACT_SERVER_CONTEXT_TYPE, } from 'shared/ReactSymbols'; import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry';