Skip to content

Commit

Permalink
Add Server Context deprecation warning
Browse files Browse the repository at this point in the history
  • Loading branch information
sebmarkbage committed Sep 27, 2023
1 parent bff6be8 commit 093cc1a
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 36 deletions.
48 changes: 23 additions & 25 deletions packages/react-client/src/__tests__/ReactFlight-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 emposes on all apps.',
{withoutStack: !withStack},
);
return ctx;
}

function clientReference(value) {
return Object.defineProperties(
function () {
Expand Down Expand Up @@ -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',
);
Expand All @@ -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 (
Expand Down Expand Up @@ -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: <span>hi this is default</span>,
},
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -1294,16 +1295,13 @@ describe('ReactFlight', () => {
expect(ReactNoop).toMatchRenderedOutput(<span>hi this is server</span>);

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 <span>{React.useContext(ServerContext)}</span>;
}
Expand All @@ -1323,15 +1321,15 @@ describe('ReactFlight', () => {
let ServerContext;
function inlineLazyServerContextInitialization() {
if (!ServerContext) {
ServerContext = React.createServerContext('ServerContext', 'default');
ServerContext = createServerContext('ServerContext', 'default');
}
return ServerContext;
}

let ClientContext;
function inlineContextInitialization() {
if (!ClientContext) {
ClientContext = React.createServerContext('ServerContext', 'default');
ClientContext = createServerContext('ServerContext', 'default', true);
}
return ClientContext;
}
Expand Down
21 changes: 19 additions & 2 deletions packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3321,7 +3321,16 @@ 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 emposes on all apps.',
{
withoutStack: true,
},
);
}
return ServerContext;
}
Expand Down Expand Up @@ -5604,7 +5613,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 emposes on all apps.',
{withoutStack: true},
);
function Client() {
return use(ContextA) + use(ContextB);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 emposes 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 emposes on all apps.',
],
{withoutStack: true},
);

function ServerComponent() {
return use(ContextA) + use(ContextB);
Expand Down
7 changes: 7 additions & 0 deletions packages/react/src/ReactServerContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ export function createServerContext<T: ServerContextJSONValue>(
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 emposes on all apps.',
);
}
let wasDefined = true;
if (!ContextRegistry[globalName]) {
wasDefined = false;
Expand Down
62 changes: 55 additions & 7 deletions packages/shared/ReactServerContextRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,69 @@

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;

export function getOrCreateServerContext(
globalName: string,
): ReactServerContext<any> {
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<any> = {
$$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];
}

0 comments on commit 093cc1a

Please sign in to comment.