Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Server Context deprecation warning #27424

Merged
merged 2 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 imposes 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
19 changes: 17 additions & 2 deletions packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
Expand Down Expand Up @@ -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);
}
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 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);
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 imposes 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> = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this inlined now?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is because we don’t want to warn when this method is called which is used internally in react

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea and I plan on literally deleting the code soon so meh.

$$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];
}