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 9d5ac3680a8a0..acc3436f7c435 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 8d0fbe1609d98..2db829fd7cd9d 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 895e75359867c..4e248371ece9a 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 91a1a0c713cb3..28fa7f5a75c78 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 65af591e3651d..bdcd9f4ba65b3 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 2940c47bde46d..1da0127a131be 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 df586c6efb2cb..b4e2e830fc82e 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 f6d64c003f9e1..ae09467430da2 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 b001088f17e6d..6cf1c3f0e7e5a 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 cbf3c5fa2e3ce..dec9cbb7c3269 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 7747a25c858ed..a66ce4a9c3fd7 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 d7137e8cd7f66..72748ac26d69e 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 8ff30c810f03f..e73158d2f77ad 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 93fe3bc8395c7..0adc1d03c6b41 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 ccc589a9dfe60..491e82d89d36b 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 ccc589a9dfe60..491e82d89d36b 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 f3efac3e74d68..eca412d017b4b 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 bbfe70c26cfc4..50ad16380c14b 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 85eace536ac4e..618226a1dc4c1 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 9d632dfb0712e..c7286509b519d 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 5f992d4b03e9b..92abb8afae1f5 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 0624e693cfe16..f57017154c387 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 5997f1b02b902..1b04474a60255 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 0eaa07f839a16..01df3d8449415 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 0000000000000..36b37eb4a5da5 --- /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 0000000000000..b3c52c711572c --- /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 052fa730fd91e..649c4f7f35a15 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 4ff841bfc11ba..13cbcf7614138 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 33c770f00fed5..122f679fcfda6 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 d4dc33a4db01a..1ee0c52731f12 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 6a249ba432c72..06cf6367f556d 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 5b0d75e21460b..231f094922fe6 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 b0f6f409bb0e1..e91878c4d8a7b 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 891269ee32653..43f5673ac9612 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 12a5350083f38..2c7bc094a1a16 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 0000000000000..8d4d93ea4576b --- /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 43f42bddb91d3..9bfeca29dcb0b 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 a8e1d088664e9..11af5e6d97a5e 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 6703e2cd36539..2c293a02bed94 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 2db829fd7cd9d..54ab8aa87bfcb 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 e73158d2f77ad..a871401d9a1c0 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 649c4f7f35a15..e81a2c25ed208 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 e81a2c25ed208..912793a04da9d 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 acc3436f7c435..e43446435d54a 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 54ab8aa87bfcb..d86e9cad522d4 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 4e248371ece9a..80fc0ec603123 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 28fa7f5a75c78..28dd60365c361 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 1902dc65690c8..874c9642dc172 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 1da0127a131be..b36cd289caa86 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 dd81ec036158d..8c5d26a08c2ae 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 a66ce4a9c3fd7..7f590186de29d 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 72748ac26d69e..3795223bbcca4 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 a871401d9a1c0..480e86dbfd6f4 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 0adc1d03c6b41..78cca5e23b4db 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 d6e37f2785ff2..32421e192b149 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 50ad16380c14b..c38afe3b71418 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 4ae33c2ed594a..b7a127450abd5 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 618226a1dc4c1..85eace536ac4e 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 f57017154c387..45f82f3be649e 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 36b37eb4a5da5..b87b4b87732b9 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 912793a04da9d..ee092d508f015 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 13cbcf7614138..4ff841bfc11ba 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 d75f1f7c9b453..27115bf6b4b2f 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 2c7bc094a1a16..1173337ce8354 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 8d4d93ea4576b..1d15a9b31612f 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 fe9d77766cdb7..361d1012d80e4 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 a50a06b150169..66593062ef3b2 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 9bfeca29dcb0b..fc400a409f27c 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 699d9a571ff6d..a19447b82a105 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 0328a98aef589..2a7f42150d72a 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 b1afbda8ec594..26d690d69d3fc 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 e64d09d5d6697..b853c430146fa 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 f0d4a9acc5605..844aa0a92fae4 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 43ce351301ce5..0be032ae667f3 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 c0545d2aacf3b..44e3d1887f2ef 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 11af5e6d97a5e..8b202cd2a2fd4 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 2c293a02bed94..b46f6ce6df454 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 7f590186de29d..8f535d4692360 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 3795223bbcca4..3069d5bc9f114 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 e43446435d54a..90a960e3a616d 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 9f07d8cc999ad..e54f2adb69edf 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 0000000000000..42639302ac3b4 --- /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 d86e9cad522d4..683e4c2a3e20a 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 6cf1c3f0e7e5a..3dc563f963575 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 ee092d508f015..f6a3bf3e8102b 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 0000000000000..b9e48c085ad1f --- /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 1d15a9b31612f..42639302ac3b4 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 0000000000000..c98f12b6e35bb --- /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 3443fb276633c..de3cc15e72476 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 44e3d1887f2ef..1012db58d69ce 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 e54f2adb69edf..9f07d8cc999ad 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 42639302ac3b4..8d4d6a940fc09 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 683e4c2a3e20a..dae8d4d5858dc 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 3dc563f963575..6cf1c3f0e7e5a 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 f6a3bf3e8102b..460385be089ef 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 b9e48c085ad1f..8d4d6a940fc09 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 42639302ac3b4..8d4d6a940fc09 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 c98f12b6e35bb..f24d947430948 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 fc400a409f27c..895d86d5e30a9 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 8d4d6a940fc09..2cab7790b5f84 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 8d4d6a940fc09..2cab7790b5f84 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 8d4d6a940fc09..2cab7790b5f84 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 2cab7790b5f84..a5064da544ff1 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 2cab7790b5f84..a5064da544ff1 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 2cab7790b5f84..a5064da544ff1 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 a5064da544ff1..caa3cb381c2fc 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 a5064da544ff1..caa3cb381c2fc 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 a5064da544ff1..caa3cb381c2fc 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 5dcfbb5650e63..87dd07bd0a786 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 460385be089ef..e292bbac0abf9 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 6c1df25af2425..718765d31e63e 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 dae8d4d5858dc..54fc5e5bb22e6 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 80fc0ec603123..b418bac1f8671 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 b87b4b87732b9..97071a9b2898b 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 e292bbac0abf9..630907e3960b3 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 1173337ce8354..39b364b42f79a 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 1012db58d69ce..44e3d1887f2ef 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 b46f6ce6df454..8ed3b00458fc8 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 54fc5e5bb22e6..1cf6f8f4d4f53 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 ae09467430da2..cb1f362f79bdc 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 630907e3960b3..e22a033983aee 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 e22a033983aee..d41afca2b15f8 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 26afab426333e..7981c766f66a7 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 caa3cb381c2fc..fdb80d1f3164c 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 1cf6f8f4d4f53..1dc4f6f8db81e 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 b418bac1f8671..466d852fb6951 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 b36cd289caa86..1cd95f08fb636 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 6a1d7e089b9a6..c6f79e1fd3e44 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 450f27427dc52..72c8111854c93 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 b5e11b015caae..83608591f36c8 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 1b04474a60255..a3a79a93fbf9b 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 97071a9b2898b..bd703b357f894 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 caa3cb381c2fc..fdb80d1f3164c 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 27115bf6b4b2f..d75f1f7c9b453 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 122f679fcfda6..76326a0fe59d0 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 1ee0c52731f12..19af075993d56 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 aa25252e253b0..084aabb53c6bf 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 231f094922fe6..e9f80ade06105 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 e91878c4d8a7b..5fe9b51a56c87 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 a64384347a3a9..d13bf62941b4c 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 39b364b42f79a..12a5350083f38 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 caa3cb381c2fc..fdb80d1f3164c 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 7981c766f66a7..af8d9ad78ca4f 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 fdb80d1f3164c..0925d1f7c53ba 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 1dc4f6f8db81e..d18e10ba6cfa4 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 01397d00004fc..fb3fb7bf645f5 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 1cd95f08fb636..e2280fbd48bb8 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 c6f79e1fd3e44..745593243d9fb 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 72c8111854c93..48dceb85a1d0e 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 480e86dbfd6f4..9a516a3fec281 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 78cca5e23b4db..2a990c9b0400a 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 01df3d8449415..c333b5681de3d 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 bd703b357f894..0b12e00bfa365 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 d41afca2b15f8..b1de039578444 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 fdb80d1f3164c..790e9485f6db0 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 fdb80d1f3164c..ce2730e73fa53 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 f24d947430948..c98f12b6e35bb 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 85b382a7694a4..1939ff4bf30f7 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 895d86d5e30a9..d939b7e05fb0f 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 b107b54d9fb51..a34561f2c625a 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 af8d9ad78ca4f..aef64d5e58b19 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 5e2a144b4e29c..dd395d30d1132 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 0925d1f7c53ba..61af8e3ec3101 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 d18e10ba6cfa4..b4d1eaa162193 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 ebc6920be8d2f..cbeb1b9c9188f 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 88c1d53608372..70225e8281c90 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 790e9485f6db0..eacfbdf0640bc 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 ce2730e73fa53..a236de4b2a408 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 c98f12b6e35bb..dda738ac7439c 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 de3cc15e72476..d7d7d07c86be9 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 87dd07bd0a786..ccf0b159843c9 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 1939ff4bf30f7..591a1189914dc 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 fb3fb7bf645f5..ddcf5759be46d 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 90a960e3a616d..365565067f5d0 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 61af8e3ec3101..0000000000000 --- 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 b4d1eaa162193..9197a4c3bfe5c 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 466d852fb6951..30b3c863c7900 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 ddcf5759be46d..41baa80795500 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 874c9642dc172..7e30b1ad1ee5c 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 bdcd9f4ba65b3..f51dcb27b1439 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 cb1f362f79bdc..987c7ee27b74f 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 b8faaf44221e7..78accd1daad68 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 9a1d6945d4f25..b35809a96cb25 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 9a516a3fec281..13b75a56391c9 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 2a990c9b0400a..0909254b6f51b 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 686b2ae334a7c..cf0fb63f1936a 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 a05595624b41c..dec6c3d5cafa6 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 c7286509b519d..122a56c2a69f7 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 92abb8afae1f5..bb990c9e17945 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 b1de039578444..b74486921853a 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 eacfbdf0640bc..0000000000000 --- 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 d13bf62941b4c..a762d7d83f160 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 a236de4b2a408..0000000000000 --- 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 0000000000000..b0a5e23b230eb --- /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 0000000000000..d721a3750ad92 --- /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 d939b7e05fb0f..44305beac1cd5 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 9a1cee36a137e..d07a1b3526666 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 dd395d30d1132..5e2a144b4e29c 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 19327019e597a..ec6b09d255e8d 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 e2280fbd48bb8..57c63f402e018 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 745593243d9fb..4d45a2d2bca8f 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 48dceb85a1d0e..72d26adb5da13 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 13b75a56391c9..350b13f86ddfc 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 0909254b6f51b..c61e516ac94e8 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 491e82d89d36b..9455af19c7f97 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 491e82d89d36b..9455af19c7f97 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 83608591f36c8..c1f2a4d6629c3 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 a3a79a93fbf9b..f2d0548c2155e 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 c333b5681de3d..f65a065367f20 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 0b12e00bfa365..f8c5c833cabb1 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 44305beac1cd5..e3f37580d7d3c 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 30b3c863c7900..f112f340e7a53 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 ec6b09d255e8d..96261a03bfd7e 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 7e30b1ad1ee5c..5128ae187d950 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 f51dcb27b1439..65af591e3651d 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 57c63f402e018..eb1eb5c3dcabc 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 78accd1daad68..9b8de1df80183 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 b35809a96cb25..457088598e51b 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 4d45a2d2bca8f..26ed72cf89bf9 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 72d26adb5da13..6f5bcc1fdeb0b 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 9455af19c7f97..9fe9d09572a25 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 9455af19c7f97..9fe9d09572a25 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 c1f2a4d6629c3..4037f229e9754 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 dec6c3d5cafa6..5cb87189c975c 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 f2d0548c2155e..ffaae3141046f 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 b74486921853a..92730f6764d9c 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 b0a5e23b230eb..9b1d9120e71a4 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 d07a1b3526666..36432e56acada 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 41baa80795500..9ba18b834f4a7 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 987c7ee27b74f..ee3de448217ce 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 122a56c2a69f7..7874782e91298 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 bb990c9e17945..c088725f25909 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 45f82f3be649e..0624e693cfe16 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 92730f6764d9c..34a1015e346b8 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 9b1d9120e71a4..a45c56afe7d23 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 e3f37580d7d3c..789a0ee36897b 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 aef64d5e58b19..af8d9ad78ca4f 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 34a1015e346b8..5b966bf7609b2 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 7e56fa734b924..b823ed9133130 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 b4e2e830fc82e..df586c6efb2cb 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 9d5ac3680a8a0..acc3436f7c435 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 8d0fbe1609d98..2db829fd7cd9d 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 b7159859060e7..5ebca44b925dc 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 4d975dbfec0d5..c1183a36c06b1 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 65af591e3651d..bdcd9f4ba65b3 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 2940c47bde46d..1da0127a131be 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 df586c6efb2cb..b4e2e830fc82e 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 ca6fedf2ec63b..5ef26f57813ac 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 09a0e16a1e55a..fef0efef078ab 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 2db03f39a2cb1..ab0b17caafbe7 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 011c64f59d2c8..697423c36d917 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 48f8f14bf2e2a..0869742c8c5b8 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 8ff30c810f03f..e73158d2f77ad 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 93fe3bc8395c7..0adc1d03c6b41 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 ccc589a9dfe60..491e82d89d36b 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 ccc589a9dfe60..491e82d89d36b 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 7223ad7d052b0..1516817638c23 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 162ba457d5490..52a96dfc96d74 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 971eca0908cb6..b891c7df00a33 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 35518c9d33bf2..4b6a8b2856cb8 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 5f992d4b03e9b..92abb8afae1f5 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 0387d94ecad28..c6e735a335001 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 c3ffa8cd6abd8..170ac083b604f 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 0eaa07f839a16..01df3d8449415 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 0000000000000..36b37eb4a5da5 --- /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 0000000000000..b3c52c711572c --- /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 2b0e7304af171..3324be545d19d 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 74a90f7a02a7e..c815f4ecf7c5a 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 33c770f00fed5..122f679fcfda6 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 d4dc33a4db01a..1ee0c52731f12 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 e4946bf095b69..aa25252e253b0 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 5b0d75e21460b..231f094922fe6 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 53497831e004d..d603d2b62c454 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 b899f51c80b6e..a64384347a3a9 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 9dc7a98589e4e..17bc8108d02a7 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 0000000000000..8d4d93ea4576b --- /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 066d20552d6fc..2282c05c4b2d6 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 36432e56acada..907f34d4cae96 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 367b736931cfc..66feddb5c0181 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 2db829fd7cd9d..54ab8aa87bfcb 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 e73158d2f77ad..a871401d9a1c0 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 3324be545d19d..cc4893282be78 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 cc4893282be78..e8e64e40f8aa0 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 acc3436f7c435..e43446435d54a 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 54ab8aa87bfcb..d86e9cad522d4 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 5ebca44b925dc..415d1d99e20a8 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 c1183a36c06b1..19327019e597a 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 41c2885680437..9215a9b49aee2 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 1da0127a131be..b36cd289caa86 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 dd81ec036158d..8c5d26a08c2ae 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 697423c36d917..f5786784797ca 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 0869742c8c5b8..27cb6e4551cab 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 a871401d9a1c0..480e86dbfd6f4 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 0adc1d03c6b41..78cca5e23b4db 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 d8bb6b16e29fb..9d8528b75fe5a 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 52a96dfc96d74..8a0b2521542d9 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 5cb87189c975c..a05595624b41c 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 b891c7df00a33..971eca0908cb6 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 c6e735a335001..980652b689010 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 36b37eb4a5da5..b87b4b87732b9 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 e8e64e40f8aa0..911f28a23001c 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 c815f4ecf7c5a..74a90f7a02a7e 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 d75f1f7c9b453..27115bf6b4b2f 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 17bc8108d02a7..d200348c75db0 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 8d4d93ea4576b..1d15a9b31612f 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 d33d2a92d29c7..ff488467f04d7 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 299869d12e8ab..0192407158192 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 2282c05c4b2d6..70bd32f19c834 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 1eb2086c896ba..f44819224b208 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 e9850c400a9a7..25e21000ac4af 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 cd488a2b7e5d6..8844402412253 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 6d1dcfa842cc5..5d75422fd2c33 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 e6c46ee341f01..a9ccb8f313cc6 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 0ae82026016ac..c965a21e6bc4e 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 cf61f17054d41..a978d01631067 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 907f34d4cae96..9a1cee36a137e 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 66feddb5c0181..18b2746c0e518 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 f5786784797ca..eeb26ddda3111 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 27cb6e4551cab..31dae9464d94e 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 e43446435d54a..90a960e3a616d 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 9f07d8cc999ad..e54f2adb69edf 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 0000000000000..42639302ac3b4 --- /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 d86e9cad522d4..683e4c2a3e20a 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 fef0efef078ab..d29510d898c9d 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 911f28a23001c..33d3d16fa6852 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 0000000000000..b9e48c085ad1f --- /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 1d15a9b31612f..42639302ac3b4 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 0000000000000..c98f12b6e35bb --- /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 2874b03985b9e..1f7debf27b788 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 a978d01631067..9c73333bfd199 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 e54f2adb69edf..9f07d8cc999ad 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 42639302ac3b4..8d4d6a940fc09 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 683e4c2a3e20a..dae8d4d5858dc 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 d29510d898c9d..fef0efef078ab 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 33d3d16fa6852..9e93c730643c1 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 b9e48c085ad1f..8d4d6a940fc09 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 42639302ac3b4..8d4d6a940fc09 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 c98f12b6e35bb..f24d947430948 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 70bd32f19c834..6625b5bf850b1 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 8d4d6a940fc09..2cab7790b5f84 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 8d4d6a940fc09..2cab7790b5f84 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 8d4d6a940fc09..2cab7790b5f84 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 2cab7790b5f84..a5064da544ff1 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 2cab7790b5f84..a5064da544ff1 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 2cab7790b5f84..a5064da544ff1 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 a5064da544ff1..caa3cb381c2fc 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 a5064da544ff1..caa3cb381c2fc 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 a5064da544ff1..caa3cb381c2fc 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 04e8cb577f7ab..a41c572462ba9 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 9e93c730643c1..6bf8f20eccf40 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 54828d7ebd6aa..51015b109abd3 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 dae8d4d5858dc..54fc5e5bb22e6 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 415d1d99e20a8..09b69e30c00c4 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 b87b4b87732b9..97071a9b2898b 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 6bf8f20eccf40..1f86dfd23c166 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 d200348c75db0..2769f9e1a322a 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 9c73333bfd199..a978d01631067 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 18b2746c0e518..c5fcac2d03bdc 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 54fc5e5bb22e6..1cf6f8f4d4f53 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 5ef26f57813ac..15e991fa715c7 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 1f86dfd23c166..63b6737c7f287 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 63b6737c7f287..c40e4a891def2 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 c5fcac2d03bdc..765824d893134 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 caa3cb381c2fc..fdb80d1f3164c 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 1cf6f8f4d4f53..1dc4f6f8db81e 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 09b69e30c00c4..a651dde86e54b 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 b36cd289caa86..1cd95f08fb636 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 eeb26ddda3111..ed81d02137767 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 31dae9464d94e..21e93682fe2d9 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 8a0b2521542d9..aace3ae008115 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 170ac083b604f..e595fb2a82f59 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 97071a9b2898b..bd703b357f894 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 caa3cb381c2fc..fdb80d1f3164c 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 27115bf6b4b2f..d75f1f7c9b453 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 122f679fcfda6..76326a0fe59d0 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 1ee0c52731f12..19af075993d56 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 aa25252e253b0..084aabb53c6bf 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 231f094922fe6..e9f80ade06105 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 d603d2b62c454..0dedf2d8d90e8 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 a64384347a3a9..d13bf62941b4c 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 2769f9e1a322a..bfcaf850c69d4 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 caa3cb381c2fc..fdb80d1f3164c 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 765824d893134..0049b0a84682c 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 fdb80d1f3164c..0925d1f7c53ba 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 1dc4f6f8db81e..d18e10ba6cfa4 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 90b339bb281eb..a85830e40800a 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 1cd95f08fb636..e2280fbd48bb8 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 ed81d02137767..f04e17f74fbed 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 21e93682fe2d9..7f55803957a2c 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 480e86dbfd6f4..9a516a3fec281 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 78cca5e23b4db..2a990c9b0400a 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 01df3d8449415..c333b5681de3d 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 bd703b357f894..0b12e00bfa365 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 c40e4a891def2..1b1b562a67551 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 fdb80d1f3164c..790e9485f6db0 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 fdb80d1f3164c..ce2730e73fa53 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 f24d947430948..c98f12b6e35bb 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 0192407158192..a9147c9d1d18d 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 6625b5bf850b1..5432815232dee 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 a978d01631067..9c73333bfd199 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 0049b0a84682c..eb351b2dd0496 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 5e2a144b4e29c..dd395d30d1132 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 0925d1f7c53ba..61af8e3ec3101 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 d18e10ba6cfa4..b4d1eaa162193 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 ebc6920be8d2f..cbeb1b9c9188f 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 88c1d53608372..70225e8281c90 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 790e9485f6db0..eacfbdf0640bc 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 ce2730e73fa53..a236de4b2a408 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 c98f12b6e35bb..dda738ac7439c 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 1f7debf27b788..6f160b96ec8be 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 a41c572462ba9..57e96e6654770 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 a85830e40800a..9b741b38ccd96 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 90a960e3a616d..365565067f5d0 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 61af8e3ec3101..0000000000000 --- 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 b4d1eaa162193..9197a4c3bfe5c 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 a651dde86e54b..c9f463471bfdc 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 9b741b38ccd96..ce6121ca926be 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 9215a9b49aee2..f7156a5053548 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 bdcd9f4ba65b3..f51dcb27b1439 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 15e991fa715c7..8a1ad32a2a982 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 fef0efef078ab..ee97b8d483843 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 ab0b17caafbe7..9de275b9cfa3d 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 9a516a3fec281..13b75a56391c9 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 2a990c9b0400a..0909254b6f51b 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 1516817638c23..7223ad7d052b0 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 a05595624b41c..dec6c3d5cafa6 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 4b6a8b2856cb8..a9cc8e5091400 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 92abb8afae1f5..bb990c9e17945 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 1b1b562a67551..749c91b248553 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 eacfbdf0640bc..0000000000000 --- 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 d13bf62941b4c..a762d7d83f160 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 a236de4b2a408..0000000000000 --- 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 0000000000000..b0a5e23b230eb --- /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 0000000000000..d721a3750ad92 --- /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 5432815232dee..901e95f99b1ff 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 9a1cee36a137e..d07a1b3526666 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 dd395d30d1132..5e2a144b4e29c 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 19327019e597a..ec6b09d255e8d 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 e2280fbd48bb8..57c63f402e018 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 f04e17f74fbed..d3876668f938b 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 7f55803957a2c..c119a648223f5 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 13b75a56391c9..350b13f86ddfc 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 0909254b6f51b..c61e516ac94e8 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 491e82d89d36b..9455af19c7f97 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 491e82d89d36b..9455af19c7f97 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 aace3ae008115..243b524315b3f 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 e595fb2a82f59..3bfdd9d50cd5f 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 c333b5681de3d..f65a065367f20 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 0b12e00bfa365..f8c5c833cabb1 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 901e95f99b1ff..47a2adc38e4a9 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 c9f463471bfdc..ef07b78c783cf 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 ec6b09d255e8d..96261a03bfd7e 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 f7156a5053548..3ac5eb0d5d4c6 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 f51dcb27b1439..65af591e3651d 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 57c63f402e018..eb1eb5c3dcabc 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 ee97b8d483843..09a0e16a1e55a 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 9de275b9cfa3d..2db03f39a2cb1 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 9455af19c7f97..9fe9d09572a25 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 9455af19c7f97..9fe9d09572a25 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 243b524315b3f..5e6096c6348ee 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 dec6c3d5cafa6..5cb87189c975c 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 749c91b248553..1901a46dc852f 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 b0a5e23b230eb..9b1d9120e71a4 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 d07a1b3526666..36432e56acada 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 ce6121ca926be..a85485dd5c0b5 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 8a1ad32a2a982..c611b0a92f408 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 a9cc8e5091400..aeee2d24806db 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 bb990c9e17945..c088725f25909 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 980652b689010..0387d94ecad28 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 1901a46dc852f..e17ae9ba2808c 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 9b1d9120e71a4..a45c56afe7d23 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 47a2adc38e4a9..a3fa804df3e2e 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 e17ae9ba2808c..73849b218b334 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 9d8528b75fe5a..d8bb6b16e29fb 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 b4e2e830fc82e..df586c6efb2cb 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 d3876668f938b..6af6ff99dc5a3 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 c119a648223f5..c1c4ca2e29f9b 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 73849b218b334..e836351fc6c74 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 a762d7d83f160..264c1e1dc56d0 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 d721a3750ad92..ab8421b3c329a 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 ef07b78c783cf..74f874ad29233 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 cbeb1b9c9188f..775ad34e77207 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 70225e8281c90..665b85d183a3f 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 c49b0f2b1814c..32d49b105f450 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 14ec05bd5b460..3c6c851218acc 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 a34de4142e4ce..dcc83785117f7 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 6af6ff99dc5a3..ebc35122d3bdc 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 c1c4ca2e29f9b..0da9f32eaac5b 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 3bfdd9d50cd5f..ab740b172f18f 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 e836351fc6c74..0854ae1b402c8 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 41065c13ef067..374d4d3126d92 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 bfcaf850c69d4..9dc7a98589e4e 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 a45c56afe7d23..a561fbb92e795 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 a3fa804df3e2e..f55fb8d921bd8 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 8844402412253..0a86232c7b5ac 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 a9ccb8f313cc6..d47c02949a448 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 985b928c8928e..92d3bfd128c8a 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 365565067f5d0..e90a91d443b21 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 9f07d8cc999ad..8af1734de6b71 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 971eca0908cb6..a29a6449d8b4d 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 0387d94ecad28..b07c084eaa691 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 0854ae1b402c8..15f87ebefe0d7 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 74a90f7a02a7e..08e9cbff2f508 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 e90a91d443b21..3c431a8601275 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 3c431a8601275..1bb3298e95540 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 ff488467f04d7..c858be1e506f2 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 1bb3298e95540..a1cd5af0ce893 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 94f73b0d7a4ac..31bc2b95f77bb 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 15f87ebefe0d7..29c6be57fa5f9 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 31bc2b95f77bb..60d19ecef3f78 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 75f6db8039ab0..1c32ac0dd4d44 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 74f874ad29233..b7159859060e7 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 96261a03bfd7e..4d975dbfec0d5 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 3ac5eb0d5d4c6..41c2885680437 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 eb1eb5c3dcabc..2940c47bde46d 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 32d49b105f450..e416c73b938dd 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 ebc35122d3bdc..d1c50b42d8cbc 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 0da9f32eaac5b..137ee33cba512 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 350b13f86ddfc..e5cadd29a9b8d 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 c61e516ac94e8..2947ddf5ff4b7 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 9fe9d09572a25..ccc589a9dfe60 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 9fe9d09572a25..ccc589a9dfe60 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 5e6096c6348ee..52ccb02bad188 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 ab740b172f18f..c3ffa8cd6abd8 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 f65a065367f20..dfbb1b7749eee 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 f55fb8d921bd8..17aa509e89eb9 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 3c6c851218acc..67588c3219a92 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 dcc83785117f7..e530619d41664 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 52ccb02bad188..e9d42c20e0217 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 374d4d3126d92..e547a411009ff 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 e9d42c20e0217..dd2e09c03b210 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 f8c5c833cabb1..66c31536593bf 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 66c31536593bf..ac63b774ddcef 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 b3c52c711572c..7d2547e74b4d7 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 a1cd5af0ce893..dabb215c708f6 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 dfbb1b7749eee..4e9f01a7ac0c1 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 ac63b774ddcef..88a2eac86ca2b 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 7d2547e74b4d7..e6d57a149ba09 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 a9147c9d1d18d..6ff9305fa7b00 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 e416c73b938dd..1c607befe74b8 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 e5cadd29a9b8d..6dffd8c8c4aa6 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 2947ddf5ff4b7..a67068fceed98 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 4e9f01a7ac0c1..7931402b04b05 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 e6d57a149ba09..91cc1875e47b0 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 0dedf2d8d90e8..3ed868197b6f8 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 7931402b04b05..b4386399c3340 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 ababa89250e3c..0a8c3389de711 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 241913f91e6a7..e97b2f9b2be77 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 6dffd8c8c4aa6..8f269a4050215 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 a67068fceed98..a48c842043821 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 29c6be57fa5f9..1ea074310440b 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 36432e56acada..9c7b4e0292735 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 9c7b4e0292735..ef6e938636b23 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 ef6e938636b23..f70f5d9921a3c 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 f70f5d9921a3c..937811da6b709 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 91cc1875e47b0..2a61709b10a7b 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 1ea074310440b..eea445d5312ff 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 2a61709b10a7b..3f5abaaf0793f 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 eea445d5312ff..32a08b1eff812 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';