mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[Flight] Delete Server Context (#28225)
Server Context was never documented, and has been deprecated in https://github.com/facebook/react/pull/27424. This PR removes it completely, including the implementation code. Notably, `useContext` is removed from the shared subset, so importing it from a React Server environment would now should be a build error in environments that are able to enforce that.
This commit is contained in:
@@ -48,8 +48,6 @@ import {
|
||||
REACT_POSTPONE_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
|
||||
import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry';
|
||||
|
||||
export type {CallServerCallback};
|
||||
|
||||
type UninitializedModel = string;
|
||||
@@ -634,10 +632,6 @@ function parseModelString(
|
||||
// Symbol
|
||||
return Symbol.for(value.slice(2));
|
||||
}
|
||||
case 'P': {
|
||||
// Server Context Provider
|
||||
return getOrCreateServerContext(value.slice(2)).Provider;
|
||||
}
|
||||
case 'F': {
|
||||
// Server Reference
|
||||
const id = parseInt(value.slice(2), 16);
|
||||
|
||||
+39
-387
@@ -122,32 +122,6 @@ 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 createServerServerContext(globalName, defaultValue, withStack) {
|
||||
let ctx;
|
||||
expect(() => {
|
||||
ctx = ReactServer.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 () {
|
||||
@@ -1042,18 +1016,50 @@ describe('ReactFlight', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should warn in DEV if a a client reference is passed to useContext()', () => {
|
||||
it('should error if useContext is called()', () => {
|
||||
function ServerComponent() {
|
||||
return ReactServer.useContext();
|
||||
}
|
||||
const errors = [];
|
||||
ReactNoopFlightServer.render(<ServerComponent />, {
|
||||
onError(x) {
|
||||
errors.push(x.message);
|
||||
},
|
||||
});
|
||||
expect(errors).toEqual(['ReactServer.useContext is not a function']);
|
||||
});
|
||||
|
||||
it('should error if a context without a client reference is passed to use()', () => {
|
||||
const Context = React.createContext();
|
||||
function ServerComponent() {
|
||||
return ReactServer.use(Context);
|
||||
}
|
||||
const errors = [];
|
||||
ReactNoopFlightServer.render(<ServerComponent />, {
|
||||
onError(x) {
|
||||
errors.push(x.message);
|
||||
},
|
||||
});
|
||||
expect(errors).toEqual([
|
||||
'Cannot read a Client Context from a Server Component.',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should error if a client reference is passed to use()', () => {
|
||||
const Context = React.createContext();
|
||||
const ClientContext = clientReference(Context);
|
||||
function ServerComponent() {
|
||||
return ReactServer.useContext(ClientContext);
|
||||
return ReactServer.use(ClientContext);
|
||||
}
|
||||
expect(() => {
|
||||
const transport = ReactNoopFlightServer.render(<ServerComponent />);
|
||||
ReactNoopFlightClient.read(transport);
|
||||
}).toErrorDev('Cannot read a Client Context from a Server Component.', {
|
||||
withoutStack: true,
|
||||
const errors = [];
|
||||
ReactNoopFlightServer.render(<ServerComponent />, {
|
||||
onError(x) {
|
||||
errors.push(x.message);
|
||||
},
|
||||
});
|
||||
expect(errors).toEqual([
|
||||
'Cannot read a Client Context from a Server Component.',
|
||||
]);
|
||||
});
|
||||
|
||||
describe('Hooks', () => {
|
||||
@@ -1149,360 +1155,6 @@ describe('ReactFlight', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('ServerContext', () => {
|
||||
// @gate enableServerContext
|
||||
it('supports basic createServerContext usage', async () => {
|
||||
const ServerContext = createServerServerContext(
|
||||
'ServerContext',
|
||||
'hello from server',
|
||||
);
|
||||
function Foo() {
|
||||
const context = ReactServer.useContext(ServerContext);
|
||||
return <div>{context}</div>;
|
||||
}
|
||||
|
||||
const transport = ReactNoopFlightServer.render(<Foo />);
|
||||
await act(async () => {
|
||||
ReactNoop.render(await ReactNoopFlightClient.read(transport));
|
||||
});
|
||||
|
||||
expect(ReactNoop).toMatchRenderedOutput(<div>hello from server</div>);
|
||||
});
|
||||
|
||||
// @gate enableServerContext
|
||||
it('propagates ServerContext providers in flight', async () => {
|
||||
const ServerContext = createServerServerContext(
|
||||
'ServerContext',
|
||||
'default',
|
||||
);
|
||||
|
||||
function Foo() {
|
||||
return (
|
||||
<div>
|
||||
<ServerContext.Provider value="hi this is server">
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function Bar() {
|
||||
const context = ReactServer.useContext(ServerContext);
|
||||
return context;
|
||||
}
|
||||
|
||||
const transport = ReactNoopFlightServer.render(<Foo />);
|
||||
await act(async () => {
|
||||
ReactNoop.render(await ReactNoopFlightClient.read(transport));
|
||||
});
|
||||
|
||||
expect(ReactNoop).toMatchRenderedOutput(<div>hi this is server</div>);
|
||||
});
|
||||
|
||||
// @gate enableServerContext
|
||||
it('errors if you try passing JSX through ServerContext value', () => {
|
||||
const ServerContext = createServerServerContext('ServerContext', {
|
||||
foo: {
|
||||
bar: <span>hi this is default</span>,
|
||||
},
|
||||
});
|
||||
|
||||
function Foo() {
|
||||
return (
|
||||
<div>
|
||||
<ServerContext.Provider
|
||||
value={{
|
||||
foo: {
|
||||
bar: <span>hi this is server</span>,
|
||||
},
|
||||
}}>
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function Bar() {
|
||||
const context = ReactServer.useContext(ServerContext);
|
||||
return context.foo.bar;
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
ReactNoopFlightServer.render(<Foo />);
|
||||
}).toErrorDev('React elements are not allowed in ServerContext', {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
// @gate enableServerContext
|
||||
it('propagates ServerContext and cleans up the providers in flight', async () => {
|
||||
const ServerContext = createServerServerContext(
|
||||
'ServerContext',
|
||||
'default',
|
||||
);
|
||||
|
||||
function Foo() {
|
||||
return (
|
||||
<>
|
||||
<ServerContext.Provider value="hi this is server outer">
|
||||
<ServerContext.Provider value="hi this is server">
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
<ServerContext.Provider value="hi this is server2">
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
<ServerContext.Provider value="hi this is server outer2">
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
<Bar />
|
||||
</>
|
||||
);
|
||||
}
|
||||
function Bar() {
|
||||
const context = ReactServer.useContext(ServerContext);
|
||||
return <span>{context}</span>;
|
||||
}
|
||||
|
||||
const transport = ReactNoopFlightServer.render(<Foo />);
|
||||
await act(async () => {
|
||||
ReactNoop.render(await ReactNoopFlightClient.read(transport));
|
||||
});
|
||||
|
||||
expect(ReactNoop).toMatchRenderedOutput(
|
||||
<>
|
||||
<span>hi this is server</span>
|
||||
<span>hi this is server2</span>
|
||||
<span>hi this is server outer</span>
|
||||
<span>hi this is server outer2</span>
|
||||
<span>default</span>
|
||||
</>,
|
||||
);
|
||||
});
|
||||
|
||||
// @gate enableServerContext
|
||||
it('propagates ServerContext providers in flight after suspending', async () => {
|
||||
const ServerContext = createServerServerContext(
|
||||
'ServerContext',
|
||||
'default',
|
||||
);
|
||||
|
||||
function Foo() {
|
||||
return (
|
||||
<div>
|
||||
<ServerContext.Provider value="hi this is server">
|
||||
<React.Suspense fallback={'Loading'}>
|
||||
<Bar />
|
||||
</React.Suspense>
|
||||
</ServerContext.Provider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let resolve;
|
||||
const promise = new Promise(res => {
|
||||
resolve = () => {
|
||||
promise.unsuspend = true;
|
||||
res();
|
||||
};
|
||||
});
|
||||
|
||||
function Bar() {
|
||||
if (!promise.unsuspend) {
|
||||
Scheduler.log('suspended');
|
||||
throw promise;
|
||||
}
|
||||
Scheduler.log('rendered');
|
||||
const context = ReactServer.useContext(ServerContext);
|
||||
return context;
|
||||
}
|
||||
|
||||
const transport = ReactNoopFlightServer.render(<Foo />);
|
||||
|
||||
assertLog(['suspended']);
|
||||
|
||||
await act(async () => {
|
||||
resolve();
|
||||
await promise;
|
||||
jest.runAllImmediates();
|
||||
});
|
||||
|
||||
assertLog(['rendered']);
|
||||
|
||||
await act(async () => {
|
||||
ReactNoop.render(await ReactNoopFlightClient.read(transport));
|
||||
});
|
||||
|
||||
expect(ReactNoop).toMatchRenderedOutput(<div>hi this is server</div>);
|
||||
});
|
||||
|
||||
// @gate enableServerContext
|
||||
it('serializes ServerContext to client', async () => {
|
||||
const ServerContext = createServerServerContext(
|
||||
'ServerContext',
|
||||
'default',
|
||||
);
|
||||
const ClientContext = createServerContext('ServerContext', 'default');
|
||||
|
||||
function ClientBar() {
|
||||
Scheduler.log('ClientBar');
|
||||
const context = React.useContext(ClientContext);
|
||||
return <span>{context}</span>;
|
||||
}
|
||||
|
||||
const Bar = clientReference(ClientBar);
|
||||
|
||||
function Foo() {
|
||||
return (
|
||||
<ServerContext.Provider value="hi this is server">
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const model = {
|
||||
foo: <Foo />,
|
||||
};
|
||||
|
||||
const transport = ReactNoopFlightServer.render(model);
|
||||
|
||||
assertLog([]);
|
||||
|
||||
await act(async () => {
|
||||
const flightModel = await ReactNoopFlightClient.read(transport);
|
||||
ReactNoop.render(flightModel.foo);
|
||||
});
|
||||
|
||||
assertLog(['ClientBar']);
|
||||
expect(ReactNoop).toMatchRenderedOutput(<span>hi this is server</span>);
|
||||
|
||||
expect(() => {
|
||||
createServerContext('ServerContext', 'default');
|
||||
}).toThrow('ServerContext: ServerContext already defined');
|
||||
});
|
||||
|
||||
// @gate enableServerContext
|
||||
it('takes ServerContext from the client for refetching use cases', async () => {
|
||||
const ServerContext = createServerServerContext(
|
||||
'ServerContext',
|
||||
'default',
|
||||
);
|
||||
function Bar() {
|
||||
return <span>{ReactServer.useContext(ServerContext)}</span>;
|
||||
}
|
||||
const transport = ReactNoopFlightServer.render(<Bar />, {
|
||||
context: [['ServerContext', 'Override']],
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
const flightModel = await ReactNoopFlightClient.read(transport);
|
||||
ReactNoop.render(flightModel);
|
||||
});
|
||||
expect(ReactNoop).toMatchRenderedOutput(<span>Override</span>);
|
||||
});
|
||||
|
||||
// @gate enableServerContext
|
||||
it('sets default initial value when defined lazily on server or client', async () => {
|
||||
let ServerContext;
|
||||
function inlineLazyServerContextInitialization() {
|
||||
if (!ServerContext) {
|
||||
ServerContext = createServerServerContext('ServerContext', 'default');
|
||||
}
|
||||
return ServerContext;
|
||||
}
|
||||
|
||||
let ClientContext;
|
||||
function inlineContextInitialization() {
|
||||
if (!ClientContext) {
|
||||
ClientContext = createServerContext('ServerContext', 'default', true);
|
||||
}
|
||||
return ClientContext;
|
||||
}
|
||||
|
||||
function ClientBaz() {
|
||||
const context = inlineContextInitialization();
|
||||
const value = React.useContext(context);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
|
||||
const Baz = clientReference(ClientBaz);
|
||||
|
||||
function Bar() {
|
||||
return (
|
||||
<article>
|
||||
<div>
|
||||
{ReactServer.useContext(inlineLazyServerContextInitialization())}
|
||||
</div>
|
||||
<Baz />
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
function ServerApp() {
|
||||
const Context = inlineLazyServerContextInitialization();
|
||||
return (
|
||||
<>
|
||||
<Context.Provider value="test">
|
||||
<Bar />
|
||||
</Context.Provider>
|
||||
<Bar />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ClientApp({serverModel}) {
|
||||
return (
|
||||
<>
|
||||
{serverModel}
|
||||
<ClientBaz />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const transport = ReactNoopFlightServer.render(<ServerApp />);
|
||||
|
||||
expect(ClientContext).toBe(undefined);
|
||||
|
||||
// Reset all modules, except flight-modules which keeps the registry of Client Components
|
||||
const flightModules = require('react-noop-renderer/flight-modules');
|
||||
jest.resetModules();
|
||||
jest.mock('react', () => require('react/react.react-server'));
|
||||
jest.mock('react-noop-renderer/flight-modules', () => flightModules);
|
||||
|
||||
ReactServer = require('react');
|
||||
ReactNoopFlightServer = require('react-noop-renderer/flight-server');
|
||||
|
||||
__unmockReact();
|
||||
jest.resetModules();
|
||||
jest.mock('react-noop-renderer/flight-modules', () => flightModules);
|
||||
React = require('react');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
ReactNoopFlightClient = require('react-noop-renderer/flight-client');
|
||||
act = require('internal-test-utils').act;
|
||||
Scheduler = require('scheduler');
|
||||
|
||||
await act(async () => {
|
||||
const serverModel = await ReactNoopFlightClient.read(transport);
|
||||
ReactNoop.render(<ClientApp serverModel={serverModel} />);
|
||||
});
|
||||
|
||||
expect(ClientContext).not.toBe(ServerContext);
|
||||
|
||||
expect(ReactNoop).toMatchRenderedOutput(
|
||||
<>
|
||||
<article>
|
||||
<div>test</div>
|
||||
<div>test</div>
|
||||
</article>
|
||||
<article>
|
||||
<div>default</div>
|
||||
<div>default</div>
|
||||
</article>
|
||||
<div>default</div>
|
||||
</>,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// @gate enableTaint
|
||||
it('errors when a tainted object is serialized', async () => {
|
||||
function UserClient({user}) {
|
||||
|
||||
+2
-80
@@ -3353,62 +3353,6 @@ describe('ReactDOMFizzServer', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate enableServerContext
|
||||
it('supports ServerContext', async () => {
|
||||
let ServerContext;
|
||||
function inlineLazyServerContextInitialization() {
|
||||
if (!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.',
|
||||
);
|
||||
}
|
||||
return ServerContext;
|
||||
}
|
||||
|
||||
function Foo() {
|
||||
React.useState(); // component stack generation shouldn't reinit
|
||||
inlineLazyServerContextInitialization();
|
||||
return (
|
||||
<>
|
||||
<ServerContext.Provider value="hi this is server outer">
|
||||
<ServerContext.Provider value="hi this is server">
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
<ServerContext.Provider value="hi this is server2">
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
<ServerContext.Provider value="hi this is server outer2">
|
||||
<Bar />
|
||||
</ServerContext.Provider>
|
||||
<Bar />
|
||||
</>
|
||||
);
|
||||
}
|
||||
function Bar() {
|
||||
const context = React.useContext(inlineLazyServerContextInitialization());
|
||||
return <span>{context}</span>;
|
||||
}
|
||||
|
||||
await act(() => {
|
||||
const {pipe} = renderToPipeableStream(<Foo />);
|
||||
pipe(writable);
|
||||
});
|
||||
|
||||
expect(getVisibleChildren(container)).toEqual([
|
||||
<span>hi this is server</span>,
|
||||
<span>hi this is server2</span>,
|
||||
<span>hi this is server outer</span>,
|
||||
<span>hi this is server outer2</span>,
|
||||
<span>default</span>,
|
||||
]);
|
||||
});
|
||||
|
||||
it('Supports iterable', async () => {
|
||||
const Immutable = require('immutable');
|
||||
|
||||
@@ -5886,39 +5830,18 @@ describe('ReactDOMFizzServer', () => {
|
||||
expect(getVisibleChildren(container)).toEqual('ABC');
|
||||
});
|
||||
|
||||
// @gate enableServerContext
|
||||
it('basic use(context)', async () => {
|
||||
const ContextA = React.createContext('default');
|
||||
const ContextB = React.createContext('B');
|
||||
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);
|
||||
}
|
||||
function ServerComponent() {
|
||||
return use(ServerContext);
|
||||
}
|
||||
function Server() {
|
||||
return (
|
||||
<ServerContext.Provider value="C">
|
||||
<ServerComponent />
|
||||
</ServerContext.Provider>
|
||||
);
|
||||
}
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<ContextA.Provider value="A">
|
||||
<Client />
|
||||
</ContextA.Provider>
|
||||
<Server />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -5927,16 +5850,15 @@ describe('ReactDOMFizzServer', () => {
|
||||
const {pipe} = renderToPipeableStream(<App />);
|
||||
pipe(writable);
|
||||
});
|
||||
expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
|
||||
expect(getVisibleChildren(container)).toEqual('AB');
|
||||
|
||||
// Hydration uses a different renderer runtime (Fiber instead of Fizz).
|
||||
// We reset _currentRenderer here to not trigger a warning about multiple
|
||||
// renderers concurrently using these contexts
|
||||
ContextA._currentRenderer = null;
|
||||
ServerContext._currentRenderer = null;
|
||||
ReactDOMClient.hydrateRoot(container, <App />);
|
||||
await waitForAll([]);
|
||||
expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
|
||||
expect(getVisibleChildren(container)).toEqual('AB');
|
||||
});
|
||||
|
||||
it('use(promise) in multiple components', async () => {
|
||||
|
||||
Vendored
-2
@@ -11,7 +11,6 @@
|
||||
|
||||
import {
|
||||
REACT_CONTEXT_TYPE,
|
||||
REACT_SERVER_CONTEXT_TYPE,
|
||||
REACT_ELEMENT_TYPE,
|
||||
REACT_FORWARD_REF_TYPE,
|
||||
REACT_FRAGMENT_TYPE,
|
||||
@@ -44,7 +43,6 @@ export function typeOf(object: any): mixed {
|
||||
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:
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
|
||||
import type {ServerContextJSONValue} from 'shared/ReactTypes';
|
||||
|
||||
import {saveModule} from 'react-noop-renderer/flight-modules';
|
||||
|
||||
@@ -70,7 +69,6 @@ const ReactNoopFlightServer = ReactFlightServer({
|
||||
|
||||
type Options = {
|
||||
onError?: (error: mixed) => void,
|
||||
context?: Array<[string, ServerContextJSONValue]>,
|
||||
identifierPrefix?: string,
|
||||
};
|
||||
|
||||
@@ -81,7 +79,6 @@ function render(model: ReactClientValue, options?: Options): Destination {
|
||||
model,
|
||||
bundlerConfig,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.context : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
);
|
||||
ReactNoopFlightServer.startWork(request);
|
||||
|
||||
+4
-17
@@ -27,7 +27,6 @@ import {
|
||||
REACT_PORTAL_TYPE,
|
||||
REACT_LAZY_TYPE,
|
||||
REACT_CONTEXT_TYPE,
|
||||
REACT_SERVER_CONTEXT_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
import {ClassComponent, HostText, HostPortal, Fragment} from './ReactWorkTags';
|
||||
import isArray from 'shared/isArray';
|
||||
@@ -577,10 +576,7 @@ function createChildReconciler(
|
||||
return createChild(returnFiber, unwrapThenable(thenable), lanes);
|
||||
}
|
||||
|
||||
if (
|
||||
newChild.$$typeof === REACT_CONTEXT_TYPE ||
|
||||
newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
|
||||
) {
|
||||
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
|
||||
const context: ReactContext<mixed> = (newChild: any);
|
||||
return createChild(
|
||||
returnFiber,
|
||||
@@ -667,10 +663,7 @@ function createChildReconciler(
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
newChild.$$typeof === REACT_CONTEXT_TYPE ||
|
||||
newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
|
||||
) {
|
||||
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
|
||||
const context: ReactContext<mixed> = (newChild: any);
|
||||
return updateSlot(
|
||||
returnFiber,
|
||||
@@ -756,10 +749,7 @@ function createChildReconciler(
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
newChild.$$typeof === REACT_CONTEXT_TYPE ||
|
||||
newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
|
||||
) {
|
||||
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
|
||||
const context: ReactContext<mixed> = (newChild: any);
|
||||
return updateFromMap(
|
||||
existingChildren,
|
||||
@@ -1442,10 +1432,7 @@ function createChildReconciler(
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
newChild.$$typeof === REACT_CONTEXT_TYPE ||
|
||||
newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
|
||||
) {
|
||||
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
|
||||
const context: ReactContext<mixed> = (newChild: any);
|
||||
return reconcileChildFibersImpl(
|
||||
returnFiber,
|
||||
|
||||
@@ -73,8 +73,6 @@ export const CacheContext: ReactContext<Cache> = enableCache
|
||||
_currentValue: (null: any),
|
||||
_currentValue2: (null: any),
|
||||
_threadCount: 0,
|
||||
_defaultValue: (null: any),
|
||||
_globalName: (null: any),
|
||||
}
|
||||
: (null: any);
|
||||
|
||||
|
||||
+1
-5
@@ -46,7 +46,6 @@ import {
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
REACT_CONTEXT_TYPE,
|
||||
REACT_SERVER_CONTEXT_TYPE,
|
||||
REACT_MEMO_CACHE_SENTINEL,
|
||||
} from 'shared/ReactSymbols';
|
||||
|
||||
@@ -1072,10 +1071,7 @@ function use<T>(usable: Usable<T>): T {
|
||||
// This is a thenable.
|
||||
const thenable: Thenable<T> = (usable: any);
|
||||
return useThenable(thenable);
|
||||
} else if (
|
||||
usable.$$typeof === REACT_CONTEXT_TYPE ||
|
||||
usable.$$typeof === REACT_SERVER_CONTEXT_TYPE
|
||||
) {
|
||||
} else if (usable.$$typeof === REACT_CONTEXT_TYPE) {
|
||||
const context: ReactContext<T> = (usable: any);
|
||||
return readContext(context);
|
||||
}
|
||||
|
||||
@@ -51,8 +51,6 @@ export const HostTransitionContext: ReactContext<TransitionStatus | null> = {
|
||||
_threadCount: 0,
|
||||
Provider: (null: any),
|
||||
Consumer: (null: any),
|
||||
_defaultValue: (null: any),
|
||||
_globalName: (null: any),
|
||||
};
|
||||
|
||||
function requiredContext<Value>(c: Value | null): Value {
|
||||
|
||||
+2
-18
@@ -44,11 +44,9 @@ import {createUpdate, ForceUpdate} from './ReactFiberClassUpdateQueue';
|
||||
import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork';
|
||||
import {
|
||||
enableLazyContextPropagation,
|
||||
enableServerContext,
|
||||
enableFormActions,
|
||||
enableAsyncActions,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols';
|
||||
import {
|
||||
getHostTransitionProvider,
|
||||
HostTransitionContext,
|
||||
@@ -153,28 +151,14 @@ export function popProvider(
|
||||
const currentValue = valueCursor.current;
|
||||
|
||||
if (isPrimaryRenderer) {
|
||||
if (
|
||||
enableServerContext &&
|
||||
currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED
|
||||
) {
|
||||
context._currentValue = context._defaultValue;
|
||||
} else {
|
||||
context._currentValue = currentValue;
|
||||
}
|
||||
context._currentValue = currentValue;
|
||||
if (__DEV__) {
|
||||
const currentRenderer = rendererCursorDEV.current;
|
||||
pop(rendererCursorDEV, providerFiber);
|
||||
context._currentRenderer = currentRenderer;
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
enableServerContext &&
|
||||
currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED
|
||||
) {
|
||||
context._currentValue2 = context._defaultValue;
|
||||
} else {
|
||||
context._currentValue2 = currentValue;
|
||||
}
|
||||
context._currentValue2 = currentValue;
|
||||
if (__DEV__) {
|
||||
const currentRenderer2 = renderer2CursorDEV.current;
|
||||
pop(renderer2CursorDEV, providerFiber);
|
||||
|
||||
@@ -16,7 +16,7 @@ import type {ClientManifest} from './ReactFlightServerConfigESMBundler';
|
||||
import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
|
||||
import type {Busboy} from 'busboy';
|
||||
import type {Writable} from 'stream';
|
||||
import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes';
|
||||
import type {Thenable} from 'shared/ReactTypes';
|
||||
|
||||
import {
|
||||
createRequest,
|
||||
@@ -54,7 +54,6 @@ function createDrainHandler(destination: Destination, request: Request) {
|
||||
type Options = {
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
context?: Array<[string, ServerContextJSONValue]>,
|
||||
identifierPrefix?: string,
|
||||
};
|
||||
|
||||
@@ -72,7 +71,6 @@ function renderToPipeableStream(
|
||||
model,
|
||||
moduleBasePath,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.context : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
|
||||
import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes';
|
||||
import type {Thenable} from 'shared/ReactTypes';
|
||||
import type {ClientManifest} from './ReactFlightServerConfigTurbopackBundler';
|
||||
import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
@@ -36,7 +36,6 @@ export {
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
signal?: AbortSignal,
|
||||
context?: Array<[string, ServerContextJSONValue]>,
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
};
|
||||
@@ -50,7 +49,6 @@ function renderToReadableStream(
|
||||
model,
|
||||
turbopackMap,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.context : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
|
||||
import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes';
|
||||
import type {Thenable} from 'shared/ReactTypes';
|
||||
import type {ClientManifest} from './ReactFlightServerConfigTurbopackBundler';
|
||||
import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
@@ -36,7 +36,6 @@ export {
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
signal?: AbortSignal,
|
||||
context?: Array<[string, ServerContextJSONValue]>,
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
};
|
||||
@@ -50,7 +49,6 @@ function renderToReadableStream(
|
||||
model,
|
||||
turbopackMap,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.context : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ import type {ClientManifest} from './ReactFlightServerConfigTurbopackBundler';
|
||||
import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
|
||||
import type {Busboy} from 'busboy';
|
||||
import type {Writable} from 'stream';
|
||||
import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes';
|
||||
import type {Thenable} from 'shared/ReactTypes';
|
||||
|
||||
import {
|
||||
createRequest,
|
||||
@@ -51,7 +51,6 @@ function createDrainHandler(destination: Destination, request: Request) {
|
||||
type Options = {
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
context?: Array<[string, ServerContextJSONValue]>,
|
||||
identifierPrefix?: string,
|
||||
};
|
||||
|
||||
@@ -69,7 +68,6 @@ function renderToPipeableStream(
|
||||
model,
|
||||
turbopackMap,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.context : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
|
||||
import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes';
|
||||
import type {Thenable} from 'shared/ReactTypes';
|
||||
import type {ClientManifest} from './ReactFlightServerConfigWebpackBundler';
|
||||
import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
@@ -40,7 +40,6 @@ export {
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
signal?: AbortSignal,
|
||||
context?: Array<[string, ServerContextJSONValue]>,
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
};
|
||||
@@ -54,7 +53,6 @@ function renderToReadableStream(
|
||||
model,
|
||||
webpackMap,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.context : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
|
||||
import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes';
|
||||
import type {Thenable} from 'shared/ReactTypes';
|
||||
import type {ClientManifest} from './ReactFlightServerConfigWebpackBundler';
|
||||
import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
@@ -40,7 +40,6 @@ export {
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
signal?: AbortSignal,
|
||||
context?: Array<[string, ServerContextJSONValue]>,
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
};
|
||||
@@ -54,7 +53,6 @@ function renderToReadableStream(
|
||||
model,
|
||||
webpackMap,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.context : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ import type {ClientManifest} from './ReactFlightServerConfigWebpackBundler';
|
||||
import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
|
||||
import type {Busboy} from 'busboy';
|
||||
import type {Writable} from 'stream';
|
||||
import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes';
|
||||
import type {Thenable} from 'shared/ReactTypes';
|
||||
|
||||
import {
|
||||
createRequest,
|
||||
@@ -63,7 +63,6 @@ function createCancelHandler(request: Request, reason: string) {
|
||||
type Options = {
|
||||
onError?: (error: mixed) => void,
|
||||
onPostpone?: (reason: string) => void,
|
||||
context?: Array<[string, ServerContextJSONValue]>,
|
||||
identifierPrefix?: string,
|
||||
};
|
||||
|
||||
@@ -81,7 +80,6 @@ function renderToPipeableStream(
|
||||
model,
|
||||
webpackMap,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.context : undefined,
|
||||
options ? options.identifierPrefix : undefined,
|
||||
options ? options.onPostpone : undefined,
|
||||
);
|
||||
|
||||
-48
@@ -641,54 +641,6 @@ describe('ReactFlightDOMBrowser', () => {
|
||||
expect(container.innerHTML).toBe('ABC');
|
||||
});
|
||||
|
||||
// @gate enableServerContext
|
||||
it('basic use(context)', async () => {
|
||||
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 ReactServer.use(ContextA) + ReactServer.use(ContextB);
|
||||
}
|
||||
function Server() {
|
||||
return (
|
||||
<ContextA.Provider value="A">
|
||||
<ServerComponent />
|
||||
</ContextA.Provider>
|
||||
);
|
||||
}
|
||||
const stream = ReactServerDOMServer.renderToReadableStream(<Server />);
|
||||
const response = ReactServerDOMClient.createFromReadableStream(stream);
|
||||
|
||||
function Client() {
|
||||
return use(response);
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
await act(() => {
|
||||
// Client uses a different renderer.
|
||||
// We reset _currentRenderer here to not trigger a warning about multiple
|
||||
// renderers concurrently using this context
|
||||
ContextA._currentRenderer = null;
|
||||
root.render(<Client />);
|
||||
});
|
||||
expect(container.innerHTML).toBe('AB');
|
||||
});
|
||||
|
||||
it('use(promise) in multiple components', async () => {
|
||||
function Child({prefix}) {
|
||||
return (
|
||||
|
||||
+1
-5
@@ -40,7 +40,6 @@ import {
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import is from 'shared/objectIs';
|
||||
import {
|
||||
REACT_SERVER_CONTEXT_TYPE,
|
||||
REACT_CONTEXT_TYPE,
|
||||
REACT_MEMO_CACHE_SENTINEL,
|
||||
} from 'shared/ReactSymbols';
|
||||
@@ -745,10 +744,7 @@ function use<T>(usable: Usable<T>): T {
|
||||
// This is a thenable.
|
||||
const thenable: Thenable<T> = (usable: any);
|
||||
return unwrapThenable(thenable);
|
||||
} else if (
|
||||
usable.$$typeof === REACT_CONTEXT_TYPE ||
|
||||
usable.$$typeof === REACT_SERVER_CONTEXT_TYPE
|
||||
) {
|
||||
} else if (usable.$$typeof === REACT_CONTEXT_TYPE) {
|
||||
const context: ReactContext<T> = (usable: any);
|
||||
return readContext(context);
|
||||
}
|
||||
|
||||
+2
-11
@@ -9,7 +9,6 @@
|
||||
|
||||
import type {ReactContext} from 'shared/ReactTypes';
|
||||
|
||||
import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols';
|
||||
import {isPrimaryRenderer} from './ReactFizzConfig';
|
||||
|
||||
let rendererSigil;
|
||||
@@ -246,11 +245,7 @@ export function popProvider<T>(context: ReactContext<T>): ContextSnapshot {
|
||||
}
|
||||
if (isPrimaryRenderer) {
|
||||
const value = prevSnapshot.parentValue;
|
||||
if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) {
|
||||
prevSnapshot.context._currentValue = prevSnapshot.context._defaultValue;
|
||||
} else {
|
||||
prevSnapshot.context._currentValue = value;
|
||||
}
|
||||
prevSnapshot.context._currentValue = value;
|
||||
if (__DEV__) {
|
||||
if (
|
||||
context._currentRenderer !== undefined &&
|
||||
@@ -266,11 +261,7 @@ export function popProvider<T>(context: ReactContext<T>): ContextSnapshot {
|
||||
}
|
||||
} else {
|
||||
const value = prevSnapshot.parentValue;
|
||||
if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) {
|
||||
prevSnapshot.context._currentValue2 = prevSnapshot.context._defaultValue;
|
||||
} else {
|
||||
prevSnapshot.context._currentValue2 = value;
|
||||
}
|
||||
prevSnapshot.context._currentValue2 = value;
|
||||
if (__DEV__) {
|
||||
if (
|
||||
context._currentRenderer2 !== undefined &&
|
||||
|
||||
+1
-5
@@ -129,7 +129,6 @@ import {
|
||||
REACT_MEMO_TYPE,
|
||||
REACT_PROVIDER_TYPE,
|
||||
REACT_CONTEXT_TYPE,
|
||||
REACT_SERVER_CONTEXT_TYPE,
|
||||
REACT_SCOPE_TYPE,
|
||||
REACT_OFFSCREEN_TYPE,
|
||||
REACT_POSTPONE_TYPE,
|
||||
@@ -2270,10 +2269,7 @@ function renderNodeDestructive(
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
maybeUsable.$$typeof === REACT_CONTEXT_TYPE ||
|
||||
maybeUsable.$$typeof === REACT_SERVER_CONTEXT_TYPE
|
||||
) {
|
||||
if (maybeUsable.$$typeof === REACT_CONTEXT_TYPE) {
|
||||
const context: ReactContext<ReactNodeList> = (maybeUsable: any);
|
||||
return renderNodeDestructive(
|
||||
request,
|
||||
|
||||
+21
-37
@@ -9,13 +9,12 @@
|
||||
|
||||
import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {Request} from './ReactFlightServer';
|
||||
import type {ReactServerContext, Thenable, Usable} from 'shared/ReactTypes';
|
||||
import type {Thenable, Usable} from 'shared/ReactTypes';
|
||||
import type {ThenableState} from './ReactFlightThenable';
|
||||
import {
|
||||
REACT_SERVER_CONTEXT_TYPE,
|
||||
REACT_MEMO_CACHE_SENTINEL,
|
||||
REACT_CONTEXT_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
import {readContext as readContextImpl} from './ReactFlightNewContext';
|
||||
import {createThenableState, trackUsedThenable} from './ReactFlightThenable';
|
||||
import {isClientReference} from './ReactFlightServerConfig';
|
||||
|
||||
@@ -44,29 +43,6 @@ export function getThenableStateAfterSuspending(): null | ThenableState {
|
||||
return state;
|
||||
}
|
||||
|
||||
function readContext<T>(context: ReactServerContext<T>): T {
|
||||
if (__DEV__) {
|
||||
if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) {
|
||||
if (isClientReference(context)) {
|
||||
console.error('Cannot read a Client Context from a Server Component.');
|
||||
} else {
|
||||
console.error(
|
||||
'Only createServerContext is supported in Server Components.',
|
||||
);
|
||||
}
|
||||
}
|
||||
if (currentRequest === 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 HooksDispatcher: Dispatcher = {
|
||||
useMemo<T>(nextCreate: () => T): T {
|
||||
return nextCreate();
|
||||
@@ -77,8 +53,8 @@ export const HooksDispatcher: Dispatcher = {
|
||||
useDebugValue(): void {},
|
||||
useDeferredValue: (unsupportedHook: any),
|
||||
useTransition: (unsupportedHook: any),
|
||||
readContext,
|
||||
useContext: readContext,
|
||||
readContext: (unsupportedContext: any),
|
||||
useContext: (unsupportedContext: any),
|
||||
useReducer: (unsupportedHook: any),
|
||||
useRef: (unsupportedHook: any),
|
||||
useState: (unsupportedHook: any),
|
||||
@@ -111,6 +87,10 @@ function unsupportedRefresh(): void {
|
||||
);
|
||||
}
|
||||
|
||||
function unsupportedContext(): void {
|
||||
throw new Error('Cannot read a Client Context from a Server Component.');
|
||||
}
|
||||
|
||||
function useId(): string {
|
||||
if (currentRequest === null) {
|
||||
throw new Error('useId can only be used while React is rendering');
|
||||
@@ -138,18 +118,22 @@ function use<T>(usable: Usable<T>): T {
|
||||
thenableState = createThenableState();
|
||||
}
|
||||
return trackUsedThenable(thenableState, thenable, index);
|
||||
} else if (usable.$$typeof === REACT_SERVER_CONTEXT_TYPE) {
|
||||
const context: ReactServerContext<T> = (usable: any);
|
||||
return readContext(context);
|
||||
} else if (usable.$$typeof === REACT_CONTEXT_TYPE) {
|
||||
unsupportedContext();
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (isClientReference(usable)) {
|
||||
console.error('Cannot use() an already resolved Client Reference.');
|
||||
if (isClientReference(usable)) {
|
||||
if (usable.value != null && usable.value.$$typeof === REACT_CONTEXT_TYPE) {
|
||||
// Show a more specific message since it's a common mistake.
|
||||
throw new Error('Cannot read a Client Context from a Server Component.');
|
||||
} else {
|
||||
throw new Error('Cannot use() an already resolved Client Reference.');
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
// eslint-disable-next-line react-internal/safe-string-coercion
|
||||
'An unsupported type was passed to use(): ' + String(usable),
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-internal/safe-string-coercion
|
||||
throw new Error('An unsupported type was passed to use(): ' + String(usable));
|
||||
}
|
||||
|
||||
-269
@@ -1,269 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and 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';
|
||||
|
||||
import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols';
|
||||
import {isPrimaryRenderer} from './ReactFlightServerConfig';
|
||||
|
||||
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<T: ServerContextJSONValue> = {
|
||||
parent: null | ContextNode<any>,
|
||||
depth: number, // Short hand to compute the depth of the tree at this node.
|
||||
context: ReactServerContext<T>,
|
||||
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<any>;
|
||||
|
||||
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<any>): void {
|
||||
if (isPrimaryRenderer) {
|
||||
prev.context._currentValue = prev.parentValue;
|
||||
} else {
|
||||
prev.context._currentValue2 = prev.parentValue;
|
||||
}
|
||||
}
|
||||
|
||||
function pushNode(next: ContextNode<any>): void {
|
||||
if (isPrimaryRenderer) {
|
||||
next.context._currentValue = next.value;
|
||||
} else {
|
||||
next.context._currentValue2 = next.value;
|
||||
}
|
||||
}
|
||||
|
||||
function popToNearestCommonAncestor(
|
||||
prev: ContextNode<any>,
|
||||
next: ContextNode<any>,
|
||||
): 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<any>): void {
|
||||
popNode(prev);
|
||||
const parentPrev = prev.parent;
|
||||
if (parentPrev !== null) {
|
||||
popAllPrevious(parentPrev);
|
||||
}
|
||||
}
|
||||
|
||||
function pushAllNext(next: ContextNode<any>): void {
|
||||
const parentNext = next.parent;
|
||||
if (parentNext !== null) {
|
||||
pushAllNext(parentNext);
|
||||
}
|
||||
pushNode(next);
|
||||
}
|
||||
|
||||
function popPreviousToCommonLevel(
|
||||
prev: ContextNode<any>,
|
||||
next: ContextNode<any>,
|
||||
): 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<any>,
|
||||
next: ContextNode<any>,
|
||||
): 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[incompatible-call]: 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<T: ServerContextJSONValue>(
|
||||
context: ReactServerContext<T>,
|
||||
nextValue: T,
|
||||
): ContextSnapshot {
|
||||
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;
|
||||
}
|
||||
}
|
||||
const prevNode = currentActiveSnapshot;
|
||||
const newNode: ContextNode<T> = {
|
||||
parent: prevNode,
|
||||
depth: prevNode === null ? 0 : prevNode.depth + 1,
|
||||
context: context,
|
||||
parentValue: prevValue,
|
||||
value: nextValue,
|
||||
};
|
||||
currentActiveSnapshot = newNode;
|
||||
return newNode;
|
||||
}
|
||||
|
||||
export function popProvider(): 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 (isPrimaryRenderer) {
|
||||
const value = prevSnapshot.parentValue;
|
||||
if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) {
|
||||
prevSnapshot.context._currentValue = prevSnapshot.context._defaultValue;
|
||||
} else {
|
||||
prevSnapshot.context._currentValue = value;
|
||||
}
|
||||
} else {
|
||||
const value = prevSnapshot.parentValue;
|
||||
if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) {
|
||||
prevSnapshot.context._currentValue2 = prevSnapshot.context._defaultValue;
|
||||
} else {
|
||||
prevSnapshot.context._currentValue2 = value;
|
||||
}
|
||||
}
|
||||
return (currentActiveSnapshot = prevSnapshot.parent);
|
||||
}
|
||||
|
||||
export function getActiveContext(): ContextSnapshot {
|
||||
return currentActiveSnapshot;
|
||||
}
|
||||
|
||||
export function readContext<T>(context: ReactServerContext<T>): T {
|
||||
const value = isPrimaryRenderer
|
||||
? context._currentValue
|
||||
: context._currentValue2;
|
||||
return value;
|
||||
}
|
||||
+4
-166
@@ -15,7 +15,6 @@ import {
|
||||
enableBinaryFlight,
|
||||
enablePostpone,
|
||||
enableTaint,
|
||||
enableServerContext,
|
||||
enableServerComponentKeys,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
@@ -46,17 +45,13 @@ import type {
|
||||
HintCode,
|
||||
HintModel,
|
||||
} from './ReactFlightServerConfig';
|
||||
import type {ContextSnapshot} from './ReactFlightNewContext';
|
||||
import type {ThenableState} from './ReactFlightThenable';
|
||||
import type {
|
||||
ReactProviderType,
|
||||
ServerContextJSONValue,
|
||||
Wakeable,
|
||||
Thenable,
|
||||
PendingThenable,
|
||||
FulfilledThenable,
|
||||
RejectedThenable,
|
||||
ReactServerContext,
|
||||
} from 'shared/ReactTypes';
|
||||
import type {LazyComponent} from 'react/src/ReactLazy';
|
||||
|
||||
@@ -82,13 +77,6 @@ import {
|
||||
resetHooksForRequest,
|
||||
} from './ReactFlightHooks';
|
||||
import {DefaultCacheDispatcher} from './flight/ReactFlightServerCache';
|
||||
import {
|
||||
pushProvider,
|
||||
popProvider,
|
||||
switchContext,
|
||||
getActiveContext,
|
||||
rootContextSnapshot,
|
||||
} from './ReactFlightNewContext';
|
||||
|
||||
import {
|
||||
getIteratorFn,
|
||||
@@ -98,7 +86,6 @@ import {
|
||||
REACT_LAZY_TYPE,
|
||||
REACT_MEMO_TYPE,
|
||||
REACT_POSTPONE_TYPE,
|
||||
REACT_PROVIDER_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
|
||||
import {
|
||||
@@ -110,7 +97,6 @@ import {
|
||||
objectName,
|
||||
} from 'shared/ReactSerializationErrors';
|
||||
|
||||
import {getOrCreateServerContext} from 'shared/ReactServerContextRegistry';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import ReactServerSharedInternals from './ReactServerSharedInternals';
|
||||
import isArray from 'shared/isArray';
|
||||
@@ -153,7 +139,6 @@ export type ReactClientValue =
|
||||
// subtype, so the receiver can only accept once of these.
|
||||
| React$Element<string>
|
||||
| React$Element<ClientReference<any> & any>
|
||||
| ReactServerContext<any>
|
||||
| string
|
||||
| boolean
|
||||
| number
|
||||
@@ -184,7 +169,6 @@ type Task = {
|
||||
toJSON: (key: string, value: ReactClientValue) => ReactJSONValue,
|
||||
keyPath: null | string, // parent server component keys
|
||||
implicitSlot: boolean, // true if the root server component of this sequence had a null key
|
||||
context: ContextSnapshot,
|
||||
thenableState: ThenableState | null,
|
||||
};
|
||||
|
||||
@@ -209,7 +193,6 @@ export type Request = {
|
||||
writtenSymbols: Map<symbol, number>,
|
||||
writtenClientReferences: Map<ClientReferenceKey, number>,
|
||||
writtenServerReferences: Map<ServerReference<any>, number>,
|
||||
writtenProviders: Map<string, number>,
|
||||
writtenObjects: WeakMap<Reference, number>, // -1 means "seen" but not outlined.
|
||||
identifierPrefix: string,
|
||||
identifierCount: number,
|
||||
@@ -266,7 +249,6 @@ export function createRequest(
|
||||
model: ReactClientValue,
|
||||
bundlerConfig: ClientManifest,
|
||||
onError: void | ((error: mixed) => ?string),
|
||||
context?: Array<[string, ServerContextJSONValue]>,
|
||||
identifierPrefix?: string,
|
||||
onPostpone: void | ((reason: string) => void),
|
||||
): Request {
|
||||
@@ -307,7 +289,6 @@ export function createRequest(
|
||||
writtenSymbols: new Map(),
|
||||
writtenClientReferences: new Map(),
|
||||
writtenServerReferences: new Map(),
|
||||
writtenProviders: new Map(),
|
||||
writtenObjects: new WeakMap(),
|
||||
identifierPrefix: identifierPrefix || '',
|
||||
identifierCount: 1,
|
||||
@@ -316,15 +297,7 @@ export function createRequest(
|
||||
onPostpone: onPostpone === undefined ? defaultPostponeHandler : onPostpone,
|
||||
};
|
||||
request.pendingChunks++;
|
||||
const rootContext = createRootContext(context);
|
||||
const rootTask = createTask(
|
||||
request,
|
||||
model,
|
||||
null,
|
||||
false,
|
||||
rootContext,
|
||||
abortSet,
|
||||
);
|
||||
const rootTask = createTask(request, model, null, false, abortSet);
|
||||
pingedTasks.push(rootTask);
|
||||
return request;
|
||||
}
|
||||
@@ -340,14 +313,6 @@ export function resolveRequest(): null | Request {
|
||||
return null;
|
||||
}
|
||||
|
||||
function createRootContext(
|
||||
reqContext?: Array<[string, ServerContextJSONValue]>,
|
||||
) {
|
||||
return importServerContexts(reqContext);
|
||||
}
|
||||
|
||||
const POP = {};
|
||||
|
||||
function serializeThenable(
|
||||
request: Request,
|
||||
task: Task,
|
||||
@@ -359,7 +324,6 @@ function serializeThenable(
|
||||
null,
|
||||
task.keyPath, // the server component sequence continues through Promise-as-a-child.
|
||||
task.implicitSlot,
|
||||
task.context,
|
||||
request.abortableTasks,
|
||||
);
|
||||
|
||||
@@ -735,33 +699,6 @@ function renderElement(
|
||||
case REACT_MEMO_TYPE: {
|
||||
return renderElement(request, task, type.type, key, ref, props);
|
||||
}
|
||||
case REACT_PROVIDER_TYPE: {
|
||||
if (enableServerContext) {
|
||||
task.context = 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) {
|
||||
console.error(
|
||||
'ServerContext can only have a value prop and children. Found: %s',
|
||||
JSON.stringify(extraKeys),
|
||||
);
|
||||
}
|
||||
}
|
||||
return renderClientElement(
|
||||
task,
|
||||
type,
|
||||
key,
|
||||
// Rely on __popProvider being serialized last to pop the provider.
|
||||
{value: props.value, children: props.children, __pop: POP},
|
||||
);
|
||||
}
|
||||
// Fallthrough
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
@@ -783,17 +720,13 @@ function createTask(
|
||||
model: ReactClientValue,
|
||||
keyPath: null | string,
|
||||
implicitSlot: boolean,
|
||||
context: ContextSnapshot,
|
||||
abortSet: Set<Task>,
|
||||
): Task {
|
||||
const id = request.nextChunkId++;
|
||||
if (typeof model === 'object' && model !== null) {
|
||||
// If we're about to write this into a new task we can assign it an ID early so that
|
||||
// any other references can refer to the value we're about to write.
|
||||
if (
|
||||
enableServerComponentKeys &&
|
||||
(keyPath !== null || implicitSlot || context !== rootContextSnapshot)
|
||||
) {
|
||||
if (enableServerComponentKeys && (keyPath !== null || implicitSlot)) {
|
||||
// If we're in some kind of context we can't necessarily reuse this object depending
|
||||
// what parent components are used.
|
||||
} else {
|
||||
@@ -806,7 +739,6 @@ function createTask(
|
||||
model,
|
||||
keyPath,
|
||||
implicitSlot,
|
||||
context,
|
||||
ping: () => pingTask(request, task),
|
||||
toJSON: function (
|
||||
this:
|
||||
@@ -850,26 +782,6 @@ function createTask(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
enableServerContext &&
|
||||
parent[0] === REACT_ELEMENT_TYPE &&
|
||||
parent[1] &&
|
||||
(parent[1]: any).$$typeof === REACT_PROVIDER_TYPE &&
|
||||
parentPropertyName === '3'
|
||||
) {
|
||||
insideContextProps = value;
|
||||
} else if (
|
||||
insideContextProps === parent &&
|
||||
parentPropertyName === 'value'
|
||||
) {
|
||||
isInsideContextValue = true;
|
||||
} else if (
|
||||
insideContextProps === parent &&
|
||||
parentPropertyName === 'children'
|
||||
) {
|
||||
isInsideContextValue = false;
|
||||
}
|
||||
}
|
||||
return renderModel(request, task, parent, parentPropertyName, value);
|
||||
},
|
||||
@@ -899,10 +811,6 @@ function serializeSymbolReference(name: string): string {
|
||||
return '$S' + name;
|
||||
}
|
||||
|
||||
function serializeProviderReference(name: string): string {
|
||||
return '$P' + name;
|
||||
}
|
||||
|
||||
function serializeNumber(number: number): string | number {
|
||||
if (Number.isFinite(number)) {
|
||||
if (number === 0 && 1 / number === -Infinity) {
|
||||
@@ -1004,7 +912,6 @@ function outlineModel(request: Request, value: ReactClientValue): number {
|
||||
value,
|
||||
null, // The way we use outlining is for reusing an object.
|
||||
false, // It makes no sense for that use case to be contextual.
|
||||
rootContextSnapshot, // Therefore we don't pass any contextual information along.
|
||||
request.abortableTasks,
|
||||
);
|
||||
retryTask(request, newTask);
|
||||
@@ -1124,8 +1031,6 @@ function escapeStringValue(value: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
let insideContextProps = null;
|
||||
let isInsideContextValue = false;
|
||||
let modelRoot: null | ReactClientValue = false;
|
||||
|
||||
function renderModel(
|
||||
@@ -1169,7 +1074,6 @@ function renderModel(
|
||||
task.model,
|
||||
task.keyPath,
|
||||
task.implicitSlot,
|
||||
task.context,
|
||||
request.abortableTasks,
|
||||
);
|
||||
const ping = newTask.ping;
|
||||
@@ -1252,19 +1156,12 @@ function renderModelDestructive(
|
||||
if (typeof value === 'object') {
|
||||
switch ((value: any).$$typeof) {
|
||||
case REACT_ELEMENT_TYPE: {
|
||||
if (__DEV__) {
|
||||
if (enableServerContext && isInsideContextValue) {
|
||||
console.error('React elements are not allowed in ServerContext');
|
||||
}
|
||||
}
|
||||
const writtenObjects = request.writtenObjects;
|
||||
const existingId = writtenObjects.get(value);
|
||||
if (existingId !== undefined) {
|
||||
if (
|
||||
enableServerComponentKeys &&
|
||||
(task.keyPath !== null ||
|
||||
task.implicitSlot ||
|
||||
task.context !== rootContextSnapshot)
|
||||
(task.keyPath !== null || task.implicitSlot)
|
||||
) {
|
||||
// If we're in some kind of context we can't reuse the result of this render or
|
||||
// previous renders of this element. We only reuse elements if they're not wrapped
|
||||
@@ -1341,9 +1238,7 @@ function renderModelDestructive(
|
||||
if (existingId !== undefined) {
|
||||
if (
|
||||
enableServerComponentKeys &&
|
||||
(task.keyPath !== null ||
|
||||
task.implicitSlot ||
|
||||
task.context !== rootContextSnapshot)
|
||||
(task.keyPath !== null || task.implicitSlot)
|
||||
) {
|
||||
// If we're in some kind of context we can't reuse the result of this render or
|
||||
// previous renders of this element. We only reuse Promises if they're not wrapped
|
||||
@@ -1366,29 +1261,6 @@ function renderModelDestructive(
|
||||
return serializePromiseID(promiseId);
|
||||
}
|
||||
|
||||
if (enableServerContext) {
|
||||
if ((value: any).$$typeof === REACT_PROVIDER_TYPE) {
|
||||
const providerKey = ((value: any): ReactProviderType<any>)._context
|
||||
._globalName;
|
||||
const writtenProviders = request.writtenProviders;
|
||||
let providerId = writtenProviders.get(providerKey);
|
||||
if (providerId === undefined) {
|
||||
request.pendingChunks++;
|
||||
providerId = request.nextChunkId++;
|
||||
writtenProviders.set(providerKey, providerId);
|
||||
emitProviderChunk(request, providerId, providerKey);
|
||||
}
|
||||
return serializeByValueID(providerId);
|
||||
} else if (value === POP) {
|
||||
task.context = popProvider();
|
||||
if (__DEV__) {
|
||||
insideContextProps = null;
|
||||
isInsideContextValue = false;
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
}
|
||||
|
||||
if (existingId !== undefined) {
|
||||
if (modelRoot === value) {
|
||||
// This is the ID we're currently emitting so we need to write it
|
||||
@@ -1752,16 +1624,6 @@ function emitSymbolChunk(request: Request, id: number, name: string): void {
|
||||
request.completedImportChunks.push(processedChunk);
|
||||
}
|
||||
|
||||
function emitProviderChunk(
|
||||
request: Request,
|
||||
id: number,
|
||||
contextName: string,
|
||||
): void {
|
||||
const contextReference = serializeProviderReference(contextName);
|
||||
const processedChunk = encodeReferenceChunk(request, id, contextReference);
|
||||
request.completedRegularChunks.push(processedChunk);
|
||||
}
|
||||
|
||||
function emitModelChunk(request: Request, id: number, json: string): void {
|
||||
const row = id.toString(16) + ':' + json + '\n';
|
||||
const processedChunk = stringToChunk(row);
|
||||
@@ -1776,8 +1638,6 @@ function retryTask(request: Request, task: Task): void {
|
||||
return;
|
||||
}
|
||||
|
||||
const prevContext = getActiveContext();
|
||||
switchContext(task.context);
|
||||
try {
|
||||
// Track the root so we know that we have to emit this object even though it
|
||||
// already has an ID. This is needed because we might see this object twice
|
||||
@@ -1848,10 +1708,6 @@ function retryTask(request: Request, task: Task): void {
|
||||
task.status = ERRORED;
|
||||
const digest = logRecoverableError(request, x);
|
||||
emitErrorChunk(request, task.id, digest, x);
|
||||
} finally {
|
||||
if (enableServerContext) {
|
||||
switchContext(prevContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2061,21 +1917,3 @@ export function abort(request: Request, reason: mixed): void {
|
||||
fatalError(request, error);
|
||||
}
|
||||
}
|
||||
|
||||
function importServerContexts(
|
||||
contexts?: Array<[string, ServerContextJSONValue]>,
|
||||
) {
|
||||
if (enableServerContext && 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);
|
||||
}
|
||||
const importedContext = getActiveContext();
|
||||
switchContext(prevContext);
|
||||
return importedContext;
|
||||
}
|
||||
return rootContextSnapshot;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ export {
|
||||
createElement,
|
||||
createFactory,
|
||||
createRef,
|
||||
createServerContext,
|
||||
use,
|
||||
forwardRef,
|
||||
isValidElement,
|
||||
|
||||
@@ -43,7 +43,6 @@ export {
|
||||
createElement,
|
||||
createFactory,
|
||||
createRef,
|
||||
createServerContext,
|
||||
use,
|
||||
forwardRef,
|
||||
isValidElement,
|
||||
|
||||
@@ -62,7 +62,6 @@ import {
|
||||
useOptimistic,
|
||||
} from './ReactHooks';
|
||||
|
||||
import {createServerContext} from './ReactServerContext';
|
||||
import ReactSharedInternals from './ReactSharedInternalsClient';
|
||||
import {startTransition} from './ReactStartTransition';
|
||||
import {act} from './ReactAct';
|
||||
@@ -81,7 +80,6 @@ export {
|
||||
Component,
|
||||
PureComponent,
|
||||
createContext,
|
||||
createServerContext,
|
||||
forwardRef,
|
||||
lazy,
|
||||
memo,
|
||||
|
||||
@@ -31,10 +31,6 @@ export function createContext<T>(defaultValue: T): ReactContext<T> {
|
||||
// These are circular
|
||||
Provider: (null: any),
|
||||
Consumer: (null: any),
|
||||
|
||||
// Add these to use same hidden class in VM as ServerContext
|
||||
_defaultValue: (null: any),
|
||||
_globalName: (null: any),
|
||||
};
|
||||
|
||||
context.Provider = {
|
||||
|
||||
@@ -24,12 +24,10 @@ import {
|
||||
} from 'shared/ReactSymbols';
|
||||
import {cloneElement, createElement, isValidElement} from './ReactElement';
|
||||
import {createRef} from './ReactCreateRef';
|
||||
import {createServerContext} from './ReactServerContext';
|
||||
import {
|
||||
use,
|
||||
useId,
|
||||
useCallback,
|
||||
useContext,
|
||||
useDebugValue,
|
||||
useMemo,
|
||||
getCacheSignal,
|
||||
@@ -66,7 +64,6 @@ export {
|
||||
cloneElement,
|
||||
createElement,
|
||||
createRef,
|
||||
createServerContext,
|
||||
use,
|
||||
forwardRef,
|
||||
isValidElement,
|
||||
@@ -81,7 +78,6 @@ export {
|
||||
postpone as unstable_postpone,
|
||||
useId,
|
||||
useCallback,
|
||||
useContext,
|
||||
useDebugValue,
|
||||
useMemo,
|
||||
version,
|
||||
|
||||
@@ -23,15 +23,7 @@ import {
|
||||
} from 'shared/ReactSymbols';
|
||||
import {cloneElement, createElement, isValidElement} from './ReactElement';
|
||||
import {createRef} from './ReactCreateRef';
|
||||
import {createServerContext} from './ReactServerContext';
|
||||
import {
|
||||
use,
|
||||
useId,
|
||||
useCallback,
|
||||
useContext,
|
||||
useDebugValue,
|
||||
useMemo,
|
||||
} from './ReactHooks';
|
||||
import {use, useId, useCallback, useDebugValue, useMemo} from './ReactHooks';
|
||||
import {forwardRef} from './ReactForwardRef';
|
||||
import {lazy} from './ReactLazy';
|
||||
import {memo} from './ReactMemo';
|
||||
@@ -56,7 +48,6 @@ export {
|
||||
cloneElement,
|
||||
createElement,
|
||||
createRef,
|
||||
createServerContext,
|
||||
use,
|
||||
forwardRef,
|
||||
isValidElement,
|
||||
@@ -66,7 +57,6 @@ export {
|
||||
startTransition,
|
||||
useId,
|
||||
useCallback,
|
||||
useContext,
|
||||
useDebugValue,
|
||||
useMemo,
|
||||
version,
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and 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 {ContextRegistry} from './ReactServerContextRegistry';
|
||||
|
||||
export function createServerContext<T: ServerContextJSONValue>(
|
||||
globalName: string,
|
||||
defaultValue: T,
|
||||
): ReactServerContext<T> {
|
||||
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;
|
||||
const context: ReactServerContext<T> = {
|
||||
$$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,
|
||||
// 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;
|
||||
}
|
||||
|
||||
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`);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and 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} from 'shared/ReactTypes';
|
||||
|
||||
export const ContextRegistry: {
|
||||
[globalName: string]: ReactServerContext<any>,
|
||||
} = {};
|
||||
@@ -11,8 +11,6 @@ import ReactCurrentBatchConfig from './ReactCurrentBatchConfig';
|
||||
import ReactCurrentActQueue from './ReactCurrentActQueue';
|
||||
import ReactCurrentOwner from './ReactCurrentOwner';
|
||||
import ReactDebugCurrentFrame from './ReactDebugCurrentFrame';
|
||||
import {enableServerContext} from 'shared/ReactFeatureFlags';
|
||||
import {ContextRegistry} from './ReactServerContextRegistry';
|
||||
|
||||
const ReactSharedInternals = {
|
||||
ReactCurrentDispatcher,
|
||||
@@ -26,8 +24,4 @@ if (__DEV__) {
|
||||
ReactSharedInternals.ReactCurrentActQueue = ReactCurrentActQueue;
|
||||
}
|
||||
|
||||
if (enableServerContext) {
|
||||
ReactSharedInternals.ContextRegistry = ContextRegistry;
|
||||
}
|
||||
|
||||
export default ReactSharedInternals;
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
import ReactCurrentDispatcher from './ReactCurrentDispatcher';
|
||||
import ReactCurrentOwner from './ReactCurrentOwner';
|
||||
import ReactDebugCurrentFrame from './ReactDebugCurrentFrame';
|
||||
import {enableServerContext} from 'shared/ReactFeatureFlags';
|
||||
import {ContextRegistry} from './ReactServerContextRegistry';
|
||||
|
||||
const ReactSharedInternals = {
|
||||
ReactCurrentDispatcher,
|
||||
@@ -20,8 +18,4 @@ if (__DEV__) {
|
||||
ReactSharedInternals.ReactDebugCurrentFrame = ReactDebugCurrentFrame;
|
||||
}
|
||||
|
||||
if (enableServerContext) {
|
||||
ReactSharedInternals.ContextRegistry = ContextRegistry;
|
||||
}
|
||||
|
||||
export default ReactSharedInternals;
|
||||
|
||||
@@ -12,8 +12,6 @@ import ReactCurrentActQueue from '../ReactCurrentActQueue';
|
||||
import ReactCurrentOwner from '../ReactCurrentOwner';
|
||||
import ReactDebugCurrentFrame from '../ReactDebugCurrentFrame';
|
||||
import ReactCurrentBatchConfig from '../ReactCurrentBatchConfig';
|
||||
import {enableServerContext} from 'shared/ReactFeatureFlags';
|
||||
import {ContextRegistry} from '../ReactServerContextRegistry';
|
||||
|
||||
const ReactSharedInternalsClient = {
|
||||
ReactCurrentDispatcher,
|
||||
@@ -34,8 +32,4 @@ if (__DEV__) {
|
||||
ReactSharedInternalsClient.ReactDebugCurrentFrame = ReactDebugCurrentFrame;
|
||||
}
|
||||
|
||||
if (enableServerContext) {
|
||||
ReactSharedInternalsClient.ContextRegistry = ContextRegistry;
|
||||
}
|
||||
|
||||
export default ReactSharedInternalsClient;
|
||||
|
||||
@@ -240,8 +240,6 @@ export const enableAsyncDebugInfo = __EXPERIMENTAL__;
|
||||
// Track which Fiber(s) schedule render work.
|
||||
export const enableUpdaterTracking = __PROFILE__;
|
||||
|
||||
export const enableServerContext = __EXPERIMENTAL__;
|
||||
|
||||
// Internal only.
|
||||
export const enableGetInspectorDataForInstanceInProduction = false;
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and 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} from 'shared/ReactTypes';
|
||||
|
||||
import {
|
||||
REACT_PROVIDER_TYPE,
|
||||
REACT_SERVER_CONTEXT_TYPE,
|
||||
REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED,
|
||||
} from 'shared/ReactSymbols';
|
||||
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
|
||||
const ContextRegistry = ReactSharedInternals.ContextRegistry;
|
||||
|
||||
export function getOrCreateServerContext(
|
||||
globalName: string,
|
||||
): ReactServerContext<any> {
|
||||
if (!ContextRegistry[globalName]) {
|
||||
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];
|
||||
}
|
||||
@@ -19,9 +19,6 @@ export const REACT_STRICT_MODE_TYPE: symbol = Symbol.for('react.strict_mode');
|
||||
export const REACT_PROFILER_TYPE: symbol = Symbol.for('react.profiler');
|
||||
export const REACT_PROVIDER_TYPE: symbol = Symbol.for('react.provider');
|
||||
export const REACT_CONTEXT_TYPE: symbol = Symbol.for('react.context');
|
||||
export const REACT_SERVER_CONTEXT_TYPE: symbol = Symbol.for(
|
||||
'react.server_context',
|
||||
);
|
||||
export const REACT_FORWARD_REF_TYPE: symbol = Symbol.for('react.forward_ref');
|
||||
export const REACT_SUSPENSE_TYPE: symbol = Symbol.for('react.suspense');
|
||||
export const REACT_SUSPENSE_LIST_TYPE: symbol = Symbol.for(
|
||||
@@ -41,9 +38,6 @@ export const REACT_CACHE_TYPE: symbol = Symbol.for('react.cache');
|
||||
export const REACT_TRACING_MARKER_TYPE: symbol = Symbol.for(
|
||||
'react.tracing_marker',
|
||||
);
|
||||
export const REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED: symbol = Symbol.for(
|
||||
'react.default_value',
|
||||
);
|
||||
|
||||
export const REACT_MEMO_CACHE_SENTINEL: symbol = Symbol.for(
|
||||
'react.memo_cache_sentinel',
|
||||
|
||||
@@ -62,22 +62,8 @@ export type ReactContext<T> = {
|
||||
// This value may be added by application code
|
||||
// to improve DEV tooling display names
|
||||
displayName?: string,
|
||||
|
||||
// only used by ServerContext
|
||||
_defaultValue: T,
|
||||
_globalName: string,
|
||||
};
|
||||
|
||||
export type ServerContextJSONValue =
|
||||
| string
|
||||
| boolean
|
||||
| number
|
||||
| null
|
||||
| $ReadOnlyArray<ServerContextJSONValue>
|
||||
| {+[key: string]: ServerContextJSONValue};
|
||||
|
||||
export type ReactServerContext<T: any> = ReactContext<T>;
|
||||
|
||||
export type ReactPortal = {
|
||||
$$typeof: symbol | number,
|
||||
key: null | string,
|
||||
|
||||
@@ -80,7 +80,6 @@ export const allowConcurrentByDefault = true;
|
||||
export const enableCustomElementPropertySupport = false;
|
||||
|
||||
export const consoleManagedByDevToolsDuringStrictMode = false;
|
||||
export const enableServerContext = false;
|
||||
|
||||
export const enableTransitionTracing = false;
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ export const allowConcurrentByDefault = false;
|
||||
export const enableCustomElementPropertySupport = false;
|
||||
|
||||
export const consoleManagedByDevToolsDuringStrictMode = false;
|
||||
export const enableServerContext = false;
|
||||
|
||||
export const enableTransitionTracing = false;
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ export const allowConcurrentByDefault = false;
|
||||
export const enableCustomElementPropertySupport = false;
|
||||
|
||||
export const consoleManagedByDevToolsDuringStrictMode = false;
|
||||
export const enableServerContext = false;
|
||||
|
||||
export const enableTransitionTracing = false;
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@ export const enableUnifiedSyncLane = true;
|
||||
export const allowConcurrentByDefault = true;
|
||||
|
||||
export const consoleManagedByDevToolsDuringStrictMode = false;
|
||||
export const enableServerContext = false;
|
||||
|
||||
export const enableTransitionTracing = false;
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ export const allowConcurrentByDefault = true;
|
||||
export const enableCustomElementPropertySupport = false;
|
||||
|
||||
export const consoleManagedByDevToolsDuringStrictMode = false;
|
||||
export const enableServerContext = false;
|
||||
|
||||
export const enableTransitionTracing = false;
|
||||
|
||||
|
||||
@@ -100,7 +100,6 @@ export const disableTextareaChildren = __EXPERIMENTAL__;
|
||||
export const allowConcurrentByDefault = true;
|
||||
|
||||
export const consoleManagedByDevToolsDuringStrictMode = true;
|
||||
export const enableServerContext = false;
|
||||
|
||||
export const useModernStrictMode = false;
|
||||
export const enableFizzExternalRuntime = true;
|
||||
|
||||
@@ -24,14 +24,9 @@ import {
|
||||
REACT_LAZY_TYPE,
|
||||
REACT_CACHE_TYPE,
|
||||
REACT_TRACING_MARKER_TYPE,
|
||||
REACT_SERVER_CONTEXT_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
|
||||
import {
|
||||
enableServerContext,
|
||||
enableTransitionTracing,
|
||||
enableCache,
|
||||
} from './ReactFeatureFlags';
|
||||
import {enableTransitionTracing, enableCache} from './ReactFeatureFlags';
|
||||
|
||||
// Keep in sync with react-reconciler/getComponentNameFromFiber
|
||||
function getWrappedName(
|
||||
@@ -127,11 +122,6 @@ export default function getComponentNameFromType(type: mixed): string | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
case REACT_SERVER_CONTEXT_TYPE:
|
||||
if (enableServerContext) {
|
||||
const context2 = ((type: any): ReactContext<any>);
|
||||
return (context2.displayName || context2._globalName) + '.Provider';
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -486,5 +486,7 @@
|
||||
"498": "Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.",
|
||||
"499": "Only plain objects, and a few built-ins, can be passed to Server Actions. Classes or null prototypes are not supported.",
|
||||
"500": "React expected a headers state to exist when emitEarlyPreloads was called but did not find it. This suggests emitEarlyPreloads was called more than once per request. This is a bug in React.",
|
||||
"501": "The render was aborted with postpone when the shell is incomplete. Reason: %s"
|
||||
"501": "The render was aborted with postpone when the shell is incomplete. Reason: %s",
|
||||
"502": "Cannot read a Client Context from a Server Component.",
|
||||
"503": "Cannot use() an already resolved Client Reference."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user