mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Track owner on componentStorage
This commit is contained in:
committed by
Rick Hanlon
parent
7611cd82ef
commit
e471f106ab
@@ -21,6 +21,8 @@ if (typeof Blob === 'undefined') {
|
||||
if (typeof File === 'undefined') {
|
||||
global.File = require('buffer').File;
|
||||
}
|
||||
// Patch for Edge environments for global scope
|
||||
global.AsyncLocalStorage = require('async_hooks').AsyncLocalStorage;
|
||||
|
||||
// Don't wait before processing work on the server.
|
||||
// TODO: we can replace this with FlightServer.act().
|
||||
@@ -32,6 +34,7 @@ let webpackMap;
|
||||
let webpackModules;
|
||||
let webpackModuleLoading;
|
||||
let React;
|
||||
let ReactServer;
|
||||
let ReactDOMServer;
|
||||
let ReactServerDOMServer;
|
||||
let ReactServerDOMClient;
|
||||
@@ -55,6 +58,7 @@ describe('ReactFlightDOMEdge', () => {
|
||||
webpackModules = WebpackMock.webpackModules;
|
||||
webpackModuleLoading = WebpackMock.moduleLoading;
|
||||
|
||||
ReactServer = require('react');
|
||||
ReactServerDOMServer = require('react-server-dom-webpack/server');
|
||||
|
||||
jest.resetModules();
|
||||
@@ -572,6 +576,73 @@ describe('ReactFlightDOMEdge', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('supports async server component debug info as the element owner in DEV', async () => {
|
||||
function Container({children}) {
|
||||
return children;
|
||||
}
|
||||
|
||||
const promise = Promise.resolve(true);
|
||||
async function Greeting({firstName}) {
|
||||
// We can't use JSX here because it'll use the Client React.
|
||||
const child = ReactServer.createElement(
|
||||
'span',
|
||||
null,
|
||||
'Hello, ' + firstName,
|
||||
);
|
||||
// Yield the synchronous pass
|
||||
await promise;
|
||||
// We should still be able to track owner using AsyncLocalStorage.
|
||||
return ReactServer.createElement(Container, null, child);
|
||||
}
|
||||
|
||||
const model = {
|
||||
greeting: ReactServer.createElement(Greeting, {firstName: 'Seb'}),
|
||||
};
|
||||
|
||||
const stream = ReactServerDOMServer.renderToReadableStream(
|
||||
model,
|
||||
webpackMap,
|
||||
);
|
||||
|
||||
const rootModel = await ReactServerDOMClient.createFromReadableStream(
|
||||
stream,
|
||||
{
|
||||
ssrManifest: {
|
||||
moduleMap: null,
|
||||
moduleLoading: null,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const ssrStream = await ReactDOMServer.renderToReadableStream(
|
||||
rootModel.greeting,
|
||||
);
|
||||
const result = await readResult(ssrStream);
|
||||
expect(result).toEqual('<span>Hello, Seb</span>');
|
||||
|
||||
// Resolve the React Lazy wrapper which must have resolved by now.
|
||||
const lazyWrapper = rootModel.greeting;
|
||||
const greeting = lazyWrapper._init(lazyWrapper._payload);
|
||||
|
||||
// We've rendered down to the span.
|
||||
expect(greeting.type).toBe('span');
|
||||
if (__DEV__) {
|
||||
const greetInfo = {name: 'Greeting', env: 'Server', owner: null};
|
||||
expect(lazyWrapper._debugInfo).toEqual([
|
||||
greetInfo,
|
||||
{name: 'Container', env: 'Server', owner: greetInfo},
|
||||
]);
|
||||
// The owner that created the span was the outer server component.
|
||||
// We expect the debug info to be referentially equal to the owner.
|
||||
expect(greeting._owner).toBe(lazyWrapper._debugInfo[0]);
|
||||
} else {
|
||||
expect(lazyWrapper._debugInfo).toBe(undefined);
|
||||
expect(greeting._owner).toBe(
|
||||
gate(flags => flags.disableStringRefs) ? undefined : null,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// @gate enableFlightReadableStream && enableBinaryFlight
|
||||
it('should supports ReadableStreams with typed arrays', async () => {
|
||||
const buffer = new Uint8Array([
|
||||
|
||||
+23
-11
@@ -73,6 +73,8 @@ import {
|
||||
isServerReference,
|
||||
supportsRequestStorage,
|
||||
requestStorage,
|
||||
supportsComponentStorage,
|
||||
componentStorage,
|
||||
createHints,
|
||||
initAsyncDebugInfo,
|
||||
} from './ReactFlightServerConfig';
|
||||
@@ -91,7 +93,7 @@ import {
|
||||
} from './ReactFlightHooks';
|
||||
import {DefaultAsyncDispatcher} from './flight/ReactFlightAsyncDispatcher';
|
||||
|
||||
import {currentOwner, setCurrentOwner} from './flight/ReactFlightCurrentOwner';
|
||||
import {resolveOwner, setCurrentOwner} from './flight/ReactFlightCurrentOwner';
|
||||
|
||||
import {
|
||||
getIteratorFn,
|
||||
@@ -160,7 +162,7 @@ function patchConsole(consoleInst: typeof console, methodName: string) {
|
||||
// We don't currently use this id for anything but we emit it so that we can later
|
||||
// refer to previous logs in debug info to associate them with a component.
|
||||
const id = request.nextChunkId++;
|
||||
const owner: null | ReactComponentInfo = currentOwner;
|
||||
const owner: null | ReactComponentInfo = resolveOwner();
|
||||
emitConsoleChunk(request, id, methodName, owner, stack, arguments);
|
||||
}
|
||||
// $FlowFixMe[prop-missing]
|
||||
@@ -820,7 +822,11 @@ function renderFunctionComponent<Props>(
|
||||
const prevThenableState = task.thenableState;
|
||||
task.thenableState = null;
|
||||
|
||||
let componentDebugInfo: null | ReactComponentInfo = null;
|
||||
// The secondArg is always undefined in Server Components since refs error early.
|
||||
const secondArg = undefined;
|
||||
let result;
|
||||
|
||||
let componentDebugInfo: ReactComponentInfo;
|
||||
if (__DEV__) {
|
||||
if (debugID === null) {
|
||||
// We don't have a chunk to assign debug info. We need to outline this
|
||||
@@ -849,22 +855,28 @@ function renderFunctionComponent<Props>(
|
||||
outlineModel(request, componentDebugInfo);
|
||||
emitDebugChunk(request, componentDebugID, componentDebugInfo);
|
||||
}
|
||||
}
|
||||
|
||||
prepareToUseHooksForComponent(prevThenableState, componentDebugInfo);
|
||||
// The secondArg is always undefined in Server Components since refs error early.
|
||||
const secondArg = undefined;
|
||||
let result;
|
||||
if (__DEV__) {
|
||||
prepareToUseHooksForComponent(prevThenableState, componentDebugInfo);
|
||||
setCurrentOwner(componentDebugInfo);
|
||||
try {
|
||||
result = Component(props, secondArg);
|
||||
if (supportsComponentStorage) {
|
||||
// Run the component in an Async Context that tracks the current owner.
|
||||
result = componentStorage.run(
|
||||
componentDebugInfo,
|
||||
Component,
|
||||
props,
|
||||
secondArg,
|
||||
);
|
||||
} else {
|
||||
result = Component(props, secondArg);
|
||||
}
|
||||
} finally {
|
||||
setCurrentOwner(null);
|
||||
}
|
||||
} else {
|
||||
prepareToUseHooksForComponent(prevThenableState, null);
|
||||
result = Component(props, secondArg);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof result === 'object' &&
|
||||
result !== null &&
|
||||
|
||||
@@ -15,7 +15,7 @@ import {resolveRequest, getCache} from '../ReactFlightServer';
|
||||
|
||||
import {disableStringRefs} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {currentOwner} from './ReactFlightCurrentOwner';
|
||||
import {resolveOwner} from './ReactFlightCurrentOwner';
|
||||
|
||||
function resolveCache(): Map<Function, mixed> {
|
||||
const request = resolveRequest();
|
||||
@@ -39,9 +39,7 @@ export const DefaultAsyncDispatcher: AsyncDispatcher = ({
|
||||
}: any);
|
||||
|
||||
if (__DEV__) {
|
||||
DefaultAsyncDispatcher.getOwner = (): null | ReactComponentInfo => {
|
||||
return currentOwner;
|
||||
};
|
||||
DefaultAsyncDispatcher.getOwner = resolveOwner;
|
||||
} else if (!disableStringRefs) {
|
||||
// Server Components never use string refs but the JSX runtime looks for it.
|
||||
DefaultAsyncDispatcher.getOwner = (): null | ReactComponentInfo => {
|
||||
|
||||
+15
-1
@@ -9,8 +9,22 @@
|
||||
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export let currentOwner: ReactComponentInfo | null = null;
|
||||
import {
|
||||
supportsComponentStorage,
|
||||
componentStorage,
|
||||
} from '../ReactFlightServerConfig';
|
||||
|
||||
let currentOwner: ReactComponentInfo | null = null;
|
||||
|
||||
export function setCurrentOwner(componentInfo: null | ReactComponentInfo) {
|
||||
currentOwner = componentInfo;
|
||||
}
|
||||
|
||||
export function resolveOwner(): null | ReactComponentInfo {
|
||||
if (currentOwner) return currentOwner;
|
||||
if (supportsComponentStorage) {
|
||||
const owner = componentStorage.getStore();
|
||||
if (owner) return owner;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user