diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index c3c3aa420db1b..2ebbb9f2d52f5 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -98,6 +98,19 @@ describe('ReactFlight', () => { jest.restoreAllMocks(); }); + function createServerContext(globalName, defaultValue, withStack) { + let ctx; + expect(() => { + ctx = React.createServerContext(globalName, defaultValue); + }).toErrorDev( + 'Server Context is deprecated and will soon be removed. ' + + 'It was never documented and we have found it not to be useful ' + + 'enough to warrant the downside it imposes on all apps.', + {withoutStack: !withStack}, + ); + return ctx; + } + function clientReference(value) { return Object.defineProperties( function () { @@ -1063,7 +1076,7 @@ describe('ReactFlight', () => { describe('ServerContext', () => { // @gate enableServerContext it('supports basic createServerContext usage', async () => { - const ServerContext = React.createServerContext( + const ServerContext = createServerContext( 'ServerContext', 'hello from server', ); @@ -1084,10 +1097,7 @@ describe('ReactFlight', () => { // @gate enableServerContext it('propagates ServerContext providers in flight', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default', - ); + const ServerContext = createServerContext('ServerContext', 'default'); function Foo() { return ( @@ -1115,7 +1125,7 @@ describe('ReactFlight', () => { // @gate enableServerContext it('errors if you try passing JSX through ServerContext value', () => { - const ServerContext = React.createServerContext('ServerContext', { + const ServerContext = createServerContext('ServerContext', { foo: { bar: hi this is default, }, @@ -1149,10 +1159,7 @@ describe('ReactFlight', () => { // @gate enableServerContext it('propagates ServerContext and cleans up the providers in flight', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default', - ); + const ServerContext = createServerContext('ServerContext', 'default'); function Foo() { return ( @@ -1196,10 +1203,7 @@ describe('ReactFlight', () => { // @gate enableServerContext it('propagates ServerContext providers in flight after suspending', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default', - ); + const ServerContext = createServerContext('ServerContext', 'default'); function Foo() { return ( @@ -1254,10 +1258,7 @@ describe('ReactFlight', () => { // @gate enableServerContext it('serializes ServerContext to client', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default', - ); + const ServerContext = createServerContext('ServerContext', 'default'); function ClientBar() { Scheduler.log('ClientBar'); @@ -1294,16 +1295,13 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(hi this is server); expect(() => { - React.createServerContext('ServerContext', 'default'); + createServerContext('ServerContext', 'default'); }).toThrow('ServerContext: ServerContext already defined'); }); // @gate enableServerContext it('takes ServerContext from the client for refetching use cases', async () => { - const ServerContext = React.createServerContext( - 'ServerContext', - 'default', - ); + const ServerContext = createServerContext('ServerContext', 'default'); function Bar() { return {React.useContext(ServerContext)}; } @@ -1323,7 +1321,7 @@ describe('ReactFlight', () => { let ServerContext; function inlineLazyServerContextInitialization() { if (!ServerContext) { - ServerContext = React.createServerContext('ServerContext', 'default'); + ServerContext = createServerContext('ServerContext', 'default'); } return ServerContext; } @@ -1331,7 +1329,7 @@ describe('ReactFlight', () => { let ClientContext; function inlineContextInitialization() { if (!ClientContext) { - ClientContext = React.createServerContext('ServerContext', 'default'); + ClientContext = createServerContext('ServerContext', 'default', true); } return ClientContext; } diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 44f6f2e64c349..9b5e2455002af 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -3321,12 +3321,19 @@ describe('ReactDOMFizzServer', () => { let ServerContext; function inlineLazyServerContextInitialization() { if (!ServerContext) { - ServerContext = React.createServerContext('ServerContext', 'default'); + expect(() => { + ServerContext = React.createServerContext('ServerContext', 'default'); + }).toErrorDev( + 'Server Context is deprecated and will soon be removed. ' + + 'It was never documented and we have found it not to be useful ' + + 'enough to warrant the downside it imposes on all apps.', + ); } return ServerContext; } function Foo() { + React.useState(); // component stack generation shouldn't reinit inlineLazyServerContextInitialization(); return ( <> @@ -5604,7 +5611,15 @@ describe('ReactDOMFizzServer', () => { it('basic use(context)', async () => { const ContextA = React.createContext('default'); const ContextB = React.createContext('B'); - const ServerContext = React.createServerContext('ServerContext', 'default'); + let ServerContext; + expect(() => { + ServerContext = React.createServerContext('ServerContext', 'default'); + }).toErrorDev( + 'Server Context is deprecated and will soon be removed. ' + + 'It was never documented and we have found it not to be useful ' + + 'enough to warrant the downside it imposes on all apps.', + {withoutStack: true}, + ); function Client() { return use(ContextA) + use(ContextB); } diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js index 9b9697d37eeb2..af474dc148bf7 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js @@ -609,8 +609,22 @@ describe('ReactFlightDOMBrowser', () => { }); it('basic use(context)', async () => { - const ContextA = React.createServerContext('ContextA', ''); - const ContextB = React.createServerContext('ContextB', 'B'); + let ContextA; + let ContextB; + expect(() => { + ContextA = React.createServerContext('ContextA', ''); + ContextB = React.createServerContext('ContextB', 'B'); + }).toErrorDev( + [ + 'Server Context is deprecated and will soon be removed. ' + + 'It was never documented and we have found it not to be useful ' + + 'enough to warrant the downside it imposes on all apps.', + 'Server Context is deprecated and will soon be removed. ' + + 'It was never documented and we have found it not to be useful ' + + 'enough to warrant the downside it imposes on all apps.', + ], + {withoutStack: true}, + ); function ServerComponent() { return use(ContextA) + use(ContextB); diff --git a/packages/react/src/ReactServerContext.js b/packages/react/src/ReactServerContext.js index 6c1feb6cbecec..3748a05b1262d 100644 --- a/packages/react/src/ReactServerContext.js +++ b/packages/react/src/ReactServerContext.js @@ -30,6 +30,13 @@ export function createServerContext( if (!enableServerContext) { throw new Error('Not implemented.'); } + if (__DEV__) { + console.error( + 'Server Context is deprecated and will soon be removed. ' + + 'It was never documented and we have found it not to be useful ' + + 'enough to warrant the downside it imposes on all apps.', + ); + } let wasDefined = true; if (!ContextRegistry[globalName]) { wasDefined = false; diff --git a/packages/shared/ReactServerContextRegistry.js b/packages/shared/ReactServerContextRegistry.js index 8b106d890b827..f5bbbae626766 100644 --- a/packages/shared/ReactServerContextRegistry.js +++ b/packages/shared/ReactServerContextRegistry.js @@ -9,9 +9,13 @@ import type {ReactServerContext} from 'shared/ReactTypes'; -import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols'; +import { + REACT_PROVIDER_TYPE, + REACT_SERVER_CONTEXT_TYPE, + REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, +} from 'shared/ReactSymbols'; + import ReactSharedInternals from 'shared/ReactSharedInternals'; -import {createServerContext} from 'react'; const ContextRegistry = ReactSharedInternals.ContextRegistry; @@ -19,11 +23,55 @@ export function getOrCreateServerContext( globalName: string, ): ReactServerContext { if (!ContextRegistry[globalName]) { - ContextRegistry[globalName] = createServerContext( - globalName, - // $FlowFixMe[incompatible-call] function signature doesn't reflect the symbol value - REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, - ); + 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: REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, + _currentValue2: REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, + + _defaultValue: REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED, + + // 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), + 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() { + if (!hasWarnedAboutUsingConsumer) { + console.error( + 'Consumer pattern is not supported by ReactServerContext', + ); + hasWarnedAboutUsingConsumer = true; + } + return null; + }, + }, + }: any), + ); + } + ContextRegistry[globalName] = context; } return ContextRegistry[globalName]; }