Compare commits
13 Commits
gh/mofeiZ/
...
rh/test-28
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e471f106ab | ||
|
|
7611cd82ef | ||
|
|
a282ee0950 | ||
|
|
88e47069c1 | ||
|
|
1af924d03c | ||
|
|
ae602c3dcb | ||
|
|
b5150a3417 | ||
|
|
afaf8a1f67 | ||
|
|
b217f5adf1 | ||
|
|
84d0305f89 | ||
|
|
76cc7fe481 | ||
|
|
d87e6e4e07 | ||
|
|
d5d6023a01 |
@@ -537,16 +537,24 @@ describe('ReactCompositeComponent', () => {
|
||||
});
|
||||
|
||||
it('should cleanup even if render() fatals', async () => {
|
||||
const dispatcherEnabled =
|
||||
__DEV__ ||
|
||||
!gate(flags => flags.disableStringRefs) ||
|
||||
gate(flags => flags.enableCache);
|
||||
const ownerEnabled = __DEV__ || !gate(flags => flags.disableStringRefs);
|
||||
|
||||
let stashedDispatcher;
|
||||
class BadComponent extends React.Component {
|
||||
render() {
|
||||
// Stash the dispatcher that was available in render so we can check
|
||||
// that its internals also reset.
|
||||
stashedDispatcher = ReactSharedInternals.A;
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
const instance = <BadComponent />;
|
||||
expect(ReactSharedInternals.owner).toBe(
|
||||
__DEV__ || !gate(flags => flags.disableStringRefs) ? null : undefined,
|
||||
);
|
||||
expect(ReactSharedInternals.A).toBe(dispatcherEnabled ? null : undefined);
|
||||
|
||||
const root = ReactDOMClient.createRoot(document.createElement('div'));
|
||||
await expect(async () => {
|
||||
@@ -555,9 +563,16 @@ describe('ReactCompositeComponent', () => {
|
||||
});
|
||||
}).rejects.toThrow();
|
||||
|
||||
expect(ReactSharedInternals.owner).toBe(
|
||||
__DEV__ || !gate(flags => flags.disableStringRefs) ? null : undefined,
|
||||
);
|
||||
expect(ReactSharedInternals.A).toBe(dispatcherEnabled ? null : undefined);
|
||||
if (dispatcherEnabled) {
|
||||
if (ownerEnabled) {
|
||||
expect(stashedDispatcher.getOwner()).toBe(null);
|
||||
} else {
|
||||
expect(stashedDispatcher.getOwner).toBe(undefined);
|
||||
}
|
||||
} else {
|
||||
expect(stashedDispatcher).toBe(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
it('should call componentWillUnmount before unmounting', async () => {
|
||||
|
||||
@@ -61,7 +61,7 @@ import {LegacyRoot} from 'react-reconciler/src/ReactRootTags';
|
||||
import getComponentNameFromType from 'shared/getComponentNameFromType';
|
||||
import {has as hasInstance} from 'shared/ReactInstanceMap';
|
||||
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import {currentOwner} from 'react-reconciler/src/ReactFiberCurrentOwner';
|
||||
|
||||
import assign from 'shared/assign';
|
||||
|
||||
@@ -342,7 +342,7 @@ export function findDOMNode(
|
||||
componentOrElement: Element | ?React$Component<any, any>,
|
||||
): null | Element | Text {
|
||||
if (__DEV__) {
|
||||
const owner = (ReactSharedInternals.owner: any);
|
||||
const owner = currentOwner;
|
||||
if (owner !== null && owner.stateNode !== null) {
|
||||
const warnedAboutRefsInRender = owner.stateNode._warnedAboutRefsInRender;
|
||||
if (!warnedAboutRefsInRender) {
|
||||
|
||||
@@ -24,14 +24,14 @@ import {
|
||||
findHostInstanceWithWarning,
|
||||
} from 'react-reconciler/src/ReactFiberReconciler';
|
||||
import {doesFiberContain} from 'react-reconciler/src/ReactFiberTreeReflection';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import getComponentNameFromType from 'shared/getComponentNameFromType';
|
||||
import {currentOwner} from 'react-reconciler/src/ReactFiberCurrentOwner';
|
||||
|
||||
export function findHostInstance_DEPRECATED<TElementType: ElementType>(
|
||||
componentOrHandle: ?(ElementRef<TElementType> | number),
|
||||
): ?ElementRef<HostComponent<mixed>> {
|
||||
if (__DEV__) {
|
||||
const owner = ReactSharedInternals.owner;
|
||||
const owner = currentOwner;
|
||||
if (owner !== null && owner.stateNode !== null) {
|
||||
if (!owner.stateNode._warnedAboutRefsInRender) {
|
||||
console.error(
|
||||
@@ -86,7 +86,7 @@ export function findHostInstance_DEPRECATED<TElementType: ElementType>(
|
||||
|
||||
export function findNodeHandle(componentOrHandle: any): ?number {
|
||||
if (__DEV__) {
|
||||
const owner = ReactSharedInternals.owner;
|
||||
const owner = currentOwner;
|
||||
if (owner !== null && owner.stateNode !== null) {
|
||||
if (!owner.stateNode._warnedAboutRefsInRender) {
|
||||
console.error(
|
||||
|
||||
@@ -7,13 +7,17 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {CacheDispatcher} from './ReactInternalTypes';
|
||||
import type {AsyncDispatcher, Fiber} from './ReactInternalTypes';
|
||||
import type {Cache} from './ReactFiberCacheComponent';
|
||||
|
||||
import {enableCache} from 'shared/ReactFeatureFlags';
|
||||
import {readContext} from './ReactFiberNewContext';
|
||||
import {CacheContext} from './ReactFiberCacheComponent';
|
||||
|
||||
import {disableStringRefs} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {currentOwner} from './ReactFiberCurrentOwner';
|
||||
|
||||
function getCacheForType<T>(resourceType: () => T): T {
|
||||
if (!enableCache) {
|
||||
throw new Error('Not implemented.');
|
||||
@@ -27,6 +31,12 @@ function getCacheForType<T>(resourceType: () => T): T {
|
||||
return cacheForType;
|
||||
}
|
||||
|
||||
export const DefaultCacheDispatcher: CacheDispatcher = {
|
||||
export const DefaultAsyncDispatcher: AsyncDispatcher = ({
|
||||
getCacheForType,
|
||||
};
|
||||
}: any);
|
||||
|
||||
if (__DEV__ || !disableStringRefs) {
|
||||
DefaultAsyncDispatcher.getOwner = (): null | Fiber => {
|
||||
return currentOwner;
|
||||
};
|
||||
}
|
||||
@@ -91,7 +91,6 @@ import {
|
||||
Passive,
|
||||
DidDefer,
|
||||
} from './ReactFiberFlags';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import {
|
||||
debugRenderPhaseSideEffectsForStrictMode,
|
||||
disableLegacyContext,
|
||||
@@ -297,6 +296,7 @@ import {
|
||||
pushRootMarkerInstance,
|
||||
TransitionTracingMarker,
|
||||
} from './ReactFiberTracingMarkerComponent';
|
||||
import {setCurrentOwner} from './ReactFiberCurrentOwner';
|
||||
|
||||
// A special exception that's used to unwind the stack when an update flows
|
||||
// into a dehydrated boundary.
|
||||
@@ -432,7 +432,7 @@ function updateForwardRef(
|
||||
markComponentRenderStarted(workInProgress);
|
||||
}
|
||||
if (__DEV__) {
|
||||
ReactSharedInternals.owner = workInProgress;
|
||||
setCurrentOwner(workInProgress);
|
||||
setIsRendering(true);
|
||||
nextChildren = renderWithHooks(
|
||||
current,
|
||||
@@ -1150,7 +1150,7 @@ function updateFunctionComponent(
|
||||
markComponentRenderStarted(workInProgress);
|
||||
}
|
||||
if (__DEV__) {
|
||||
ReactSharedInternals.owner = workInProgress;
|
||||
setCurrentOwner(workInProgress);
|
||||
setIsRendering(true);
|
||||
nextChildren = renderWithHooks(
|
||||
current,
|
||||
@@ -1373,7 +1373,7 @@ function finishClassComponent(
|
||||
|
||||
// Rerender
|
||||
if (__DEV__ || !disableStringRefs) {
|
||||
ReactSharedInternals.owner = workInProgress;
|
||||
setCurrentOwner(workInProgress);
|
||||
}
|
||||
let nextChildren;
|
||||
if (
|
||||
@@ -3419,7 +3419,7 @@ function updateContextConsumer(
|
||||
}
|
||||
let newChildren;
|
||||
if (__DEV__) {
|
||||
ReactSharedInternals.owner = workInProgress;
|
||||
setCurrentOwner(workInProgress);
|
||||
setIsRendering(true);
|
||||
newChildren = render(newValue);
|
||||
setIsRendering(false);
|
||||
|
||||
16
packages/react-reconciler/src/ReactFiberCurrentOwner.js
vendored
Normal file
16
packages/react-reconciler/src/ReactFiberCurrentOwner.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 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 {Fiber} from './ReactInternalTypes';
|
||||
|
||||
export let currentOwner: Fiber | null = null;
|
||||
|
||||
export function setCurrentOwner(fiber: null | Fiber) {
|
||||
currentOwner = fiber;
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import type {Container, SuspenseInstance} from './ReactFiberConfig';
|
||||
import type {SuspenseState} from './ReactFiberSuspenseComponent';
|
||||
|
||||
import {get as getInstance} from 'shared/ReactInstanceMap';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
|
||||
import {
|
||||
ClassComponent,
|
||||
@@ -25,6 +24,7 @@ import {
|
||||
SuspenseComponent,
|
||||
} from './ReactWorkTags';
|
||||
import {NoFlags, Placement, Hydrating} from './ReactFiberFlags';
|
||||
import {currentOwner} from './ReactFiberCurrentOwner';
|
||||
|
||||
export function getNearestMountedFiber(fiber: Fiber): null | Fiber {
|
||||
let node = fiber;
|
||||
@@ -89,7 +89,7 @@ export function isFiberMounted(fiber: Fiber): boolean {
|
||||
|
||||
export function isMounted(component: React$Component<any, any>): boolean {
|
||||
if (__DEV__) {
|
||||
const owner = (ReactSharedInternals.owner: any);
|
||||
const owner = currentOwner;
|
||||
if (owner !== null && owner.tag === ClassComponent) {
|
||||
const ownerFiber: Fiber = owner;
|
||||
const instance = ownerFiber.stateNode;
|
||||
|
||||
@@ -203,7 +203,8 @@ import {
|
||||
resetHooksOnUnwind,
|
||||
ContextOnlyDispatcher,
|
||||
} from './ReactFiberHooks';
|
||||
import {DefaultCacheDispatcher} from './ReactFiberCache';
|
||||
import {DefaultAsyncDispatcher} from './ReactFiberAsyncDispatcher';
|
||||
import {setCurrentOwner} from './ReactFiberCurrentOwner';
|
||||
import {
|
||||
createCapturedValueAtFiber,
|
||||
type CapturedValue,
|
||||
@@ -1684,7 +1685,7 @@ function handleThrow(root: FiberRoot, thrownValue: any): void {
|
||||
resetHooksAfterThrow();
|
||||
resetCurrentDebugFiberInDEV();
|
||||
if (__DEV__ || !disableStringRefs) {
|
||||
ReactSharedInternals.owner = null;
|
||||
setCurrentOwner(null);
|
||||
}
|
||||
|
||||
if (thrownValue === SuspenseException) {
|
||||
@@ -1874,19 +1875,19 @@ function popDispatcher(prevDispatcher: any) {
|
||||
ReactSharedInternals.H = prevDispatcher;
|
||||
}
|
||||
|
||||
function pushCacheDispatcher() {
|
||||
if (enableCache) {
|
||||
const prevCacheDispatcher = ReactSharedInternals.C;
|
||||
ReactSharedInternals.C = DefaultCacheDispatcher;
|
||||
return prevCacheDispatcher;
|
||||
function pushAsyncDispatcher() {
|
||||
if (enableCache || __DEV__ || !disableStringRefs) {
|
||||
const prevAsyncDispatcher = ReactSharedInternals.A;
|
||||
ReactSharedInternals.A = DefaultAsyncDispatcher;
|
||||
return prevAsyncDispatcher;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function popCacheDispatcher(prevCacheDispatcher: any) {
|
||||
if (enableCache) {
|
||||
ReactSharedInternals.C = prevCacheDispatcher;
|
||||
function popAsyncDispatcher(prevAsyncDispatcher: any) {
|
||||
if (enableCache || __DEV__ || !disableStringRefs) {
|
||||
ReactSharedInternals.A = prevAsyncDispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1963,7 +1964,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
|
||||
const prevExecutionContext = executionContext;
|
||||
executionContext |= RenderContext;
|
||||
const prevDispatcher = pushDispatcher(root.containerInfo);
|
||||
const prevCacheDispatcher = pushCacheDispatcher();
|
||||
const prevAsyncDispatcher = pushAsyncDispatcher();
|
||||
|
||||
// If the root or lanes have changed, throw out the existing stack
|
||||
// and prepare a fresh one. Otherwise we'll continue where we left off.
|
||||
@@ -2061,7 +2062,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
|
||||
|
||||
executionContext = prevExecutionContext;
|
||||
popDispatcher(prevDispatcher);
|
||||
popCacheDispatcher(prevCacheDispatcher);
|
||||
popAsyncDispatcher(prevAsyncDispatcher);
|
||||
|
||||
if (workInProgress !== null) {
|
||||
// This is a sync render, so we should have finished the whole tree.
|
||||
@@ -2104,7 +2105,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
|
||||
const prevExecutionContext = executionContext;
|
||||
executionContext |= RenderContext;
|
||||
const prevDispatcher = pushDispatcher(root.containerInfo);
|
||||
const prevCacheDispatcher = pushCacheDispatcher();
|
||||
const prevAsyncDispatcher = pushAsyncDispatcher();
|
||||
|
||||
// If the root or lanes have changed, throw out the existing stack
|
||||
// and prepare a fresh one. Otherwise we'll continue where we left off.
|
||||
@@ -2317,7 +2318,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
|
||||
resetContextDependencies();
|
||||
|
||||
popDispatcher(prevDispatcher);
|
||||
popCacheDispatcher(prevCacheDispatcher);
|
||||
popAsyncDispatcher(prevAsyncDispatcher);
|
||||
executionContext = prevExecutionContext;
|
||||
|
||||
if (__DEV__) {
|
||||
@@ -2386,7 +2387,7 @@ function performUnitOfWork(unitOfWork: Fiber): void {
|
||||
}
|
||||
|
||||
if (__DEV__ || !disableStringRefs) {
|
||||
ReactSharedInternals.owner = null;
|
||||
setCurrentOwner(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2501,7 +2502,7 @@ function replaySuspendedUnitOfWork(unitOfWork: Fiber): void {
|
||||
}
|
||||
|
||||
if (__DEV__ || !disableStringRefs) {
|
||||
ReactSharedInternals.owner = null;
|
||||
setCurrentOwner(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2894,7 +2895,7 @@ function commitRootImpl(
|
||||
|
||||
// Reset this to null before calling lifecycles
|
||||
if (__DEV__ || !disableStringRefs) {
|
||||
ReactSharedInternals.owner = null;
|
||||
setCurrentOwner(null);
|
||||
}
|
||||
|
||||
// The commit phase is broken into several sub-phases. We do a separate pass
|
||||
|
||||
@@ -434,6 +434,8 @@ export type Dispatcher = {
|
||||
) => [Awaited<S>, (P) => void, boolean],
|
||||
};
|
||||
|
||||
export type CacheDispatcher = {
|
||||
export type AsyncDispatcher = {
|
||||
getCacheForType: <T>(resourceType: () => T) => T,
|
||||
// DEV-only (or !disableStringRefs)
|
||||
getOwner: () => null | Fiber | ReactComponentInfo,
|
||||
};
|
||||
|
||||
@@ -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([
|
||||
|
||||
27
packages/react-server/src/ReactFizzAsyncDispatcher.js
vendored
Normal file
27
packages/react-server/src/ReactFizzAsyncDispatcher.js
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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 {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
|
||||
import {disableStringRefs} from 'shared/ReactFeatureFlags';
|
||||
|
||||
function getCacheForType<T>(resourceType: () => T): T {
|
||||
throw new Error('Not implemented.');
|
||||
}
|
||||
|
||||
export const DefaultAsyncDispatcher: AsyncDispatcher = ({
|
||||
getCacheForType,
|
||||
}: any);
|
||||
|
||||
if (__DEV__ || !disableStringRefs) {
|
||||
// Fizz never tracks owner but the JSX runtime looks for this.
|
||||
DefaultAsyncDispatcher.getOwner = (): null => {
|
||||
return null;
|
||||
};
|
||||
}
|
||||
18
packages/react-server/src/ReactFizzCache.js
vendored
18
packages/react-server/src/ReactFizzCache.js
vendored
@@ -1,18 +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 {CacheDispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
|
||||
function getCacheForType<T>(resourceType: () => T): T {
|
||||
throw new Error('Not implemented.');
|
||||
}
|
||||
|
||||
export const DefaultCacheDispatcher: CacheDispatcher = {
|
||||
getCacheForType,
|
||||
};
|
||||
13
packages/react-server/src/ReactFizzServer.js
vendored
13
packages/react-server/src/ReactFizzServer.js
vendored
@@ -109,7 +109,7 @@ import {
|
||||
getActionStateCount,
|
||||
getActionStateMatchingIndex,
|
||||
} from './ReactFizzHooks';
|
||||
import {DefaultCacheDispatcher} from './ReactFizzCache';
|
||||
import {DefaultAsyncDispatcher} from './ReactFizzAsyncDispatcher';
|
||||
import {getStackByComponentStackNode} from './ReactFizzComponentStack';
|
||||
import {emptyTreeContext, pushTreeContext} from './ReactFizzTreeContext';
|
||||
|
||||
@@ -144,6 +144,7 @@ import {
|
||||
enableRenderableContext,
|
||||
enableRefAsProp,
|
||||
disableDefaultPropsExceptForClasses,
|
||||
disableStringRefs,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import assign from 'shared/assign';
|
||||
@@ -3663,10 +3664,10 @@ export function performWork(request: Request): void {
|
||||
const prevContext = getActiveContext();
|
||||
const prevDispatcher = ReactSharedInternals.H;
|
||||
ReactSharedInternals.H = HooksDispatcher;
|
||||
let prevCacheDispatcher = null;
|
||||
if (enableCache) {
|
||||
prevCacheDispatcher = ReactSharedInternals.C;
|
||||
ReactSharedInternals.C = DefaultCacheDispatcher;
|
||||
let prevAsyncDispatcher = null;
|
||||
if (enableCache || __DEV__ || !disableStringRefs) {
|
||||
prevAsyncDispatcher = ReactSharedInternals.A;
|
||||
ReactSharedInternals.A = DefaultAsyncDispatcher;
|
||||
}
|
||||
|
||||
const prevRequest = currentRequest;
|
||||
@@ -3698,7 +3699,7 @@ export function performWork(request: Request): void {
|
||||
setCurrentResumableState(prevResumableState);
|
||||
ReactSharedInternals.H = prevDispatcher;
|
||||
if (enableCache) {
|
||||
ReactSharedInternals.C = prevCacheDispatcher;
|
||||
ReactSharedInternals.A = prevAsyncDispatcher;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
|
||||
46
packages/react-server/src/ReactFlightServer.js
vendored
46
packages/react-server/src/ReactFlightServer.js
vendored
@@ -73,6 +73,8 @@ import {
|
||||
isServerReference,
|
||||
supportsRequestStorage,
|
||||
requestStorage,
|
||||
supportsComponentStorage,
|
||||
componentStorage,
|
||||
createHints,
|
||||
initAsyncDebugInfo,
|
||||
} from './ReactFlightServerConfig';
|
||||
@@ -89,7 +91,9 @@ import {
|
||||
getThenableStateAfterSuspending,
|
||||
resetHooksForRequest,
|
||||
} from './ReactFlightHooks';
|
||||
import {DefaultCacheDispatcher} from './flight/ReactFlightServerCache';
|
||||
import {DefaultAsyncDispatcher} from './flight/ReactFlightAsyncDispatcher';
|
||||
|
||||
import {resolveOwner, setCurrentOwner} from './flight/ReactFlightCurrentOwner';
|
||||
|
||||
import {
|
||||
getIteratorFn,
|
||||
@@ -158,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 = ReactSharedInternals.owner;
|
||||
const owner: null | ReactComponentInfo = resolveOwner();
|
||||
emitConsoleChunk(request, id, methodName, owner, stack, arguments);
|
||||
}
|
||||
// $FlowFixMe[prop-missing]
|
||||
@@ -358,14 +362,14 @@ export function createRequest(
|
||||
environmentName: void | string,
|
||||
): Request {
|
||||
if (
|
||||
ReactSharedInternals.C !== null &&
|
||||
ReactSharedInternals.C !== DefaultCacheDispatcher
|
||||
ReactSharedInternals.A !== null &&
|
||||
ReactSharedInternals.A !== DefaultAsyncDispatcher
|
||||
) {
|
||||
throw new Error(
|
||||
'Currently React only supports one RSC renderer at a time.',
|
||||
);
|
||||
}
|
||||
ReactSharedInternals.C = DefaultCacheDispatcher;
|
||||
ReactSharedInternals.A = DefaultAsyncDispatcher;
|
||||
|
||||
const abortSet: Set<Task> = new Set();
|
||||
const pingedTasks: Array<Task> = [];
|
||||
@@ -818,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
|
||||
@@ -847,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__) {
|
||||
ReactSharedInternals.owner = componentDebugInfo;
|
||||
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 {
|
||||
ReactSharedInternals.owner = null;
|
||||
setCurrentOwner(null);
|
||||
}
|
||||
} else {
|
||||
prepareToUseHooksForComponent(prevThenableState, null);
|
||||
result = Component(props, secondArg);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof result === 'object' &&
|
||||
result !== null &&
|
||||
|
||||
@@ -7,10 +7,16 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {CacheDispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
|
||||
import {resolveRequest, getCache} from '../ReactFlightServer';
|
||||
|
||||
import {disableStringRefs} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {resolveOwner} from './ReactFlightCurrentOwner';
|
||||
|
||||
function resolveCache(): Map<Function, mixed> {
|
||||
const request = resolveRequest();
|
||||
if (request) {
|
||||
@@ -19,7 +25,7 @@ function resolveCache(): Map<Function, mixed> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
export const DefaultCacheDispatcher: CacheDispatcher = {
|
||||
export const DefaultAsyncDispatcher: AsyncDispatcher = ({
|
||||
getCacheForType<T>(resourceType: () => T): T {
|
||||
const cache = resolveCache();
|
||||
let entry: T | void = (cache.get(resourceType): any);
|
||||
@@ -30,4 +36,13 @@ export const DefaultCacheDispatcher: CacheDispatcher = {
|
||||
}
|
||||
return entry;
|
||||
},
|
||||
};
|
||||
}: any);
|
||||
|
||||
if (__DEV__) {
|
||||
DefaultAsyncDispatcher.getOwner = resolveOwner;
|
||||
} else if (!disableStringRefs) {
|
||||
// Server Components never use string refs but the JSX runtime looks for it.
|
||||
DefaultAsyncDispatcher.getOwner = (): null | ReactComponentInfo => {
|
||||
return null;
|
||||
};
|
||||
}
|
||||
30
packages/react-server/src/flight/ReactFlightCurrentOwner.js
vendored
Normal file
30
packages/react-server/src/flight/ReactFlightCurrentOwner.js
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from '../ReactFlightServerConfigBundlerCustom';
|
||||
|
||||
@@ -23,6 +24,10 @@ export const isPrimaryRenderer = false;
|
||||
export const supportsRequestStorage = false;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
||||
|
||||
export const supportsComponentStorage = false;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
(null: any);
|
||||
|
||||
export function createHints(): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6,15 +6,18 @@
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
import {AsyncLocalStorage} from 'async_hooks';
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-esm/src/ReactFlightServerConfigESMBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
|
||||
export const supportsRequestStorage = true;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||
new AsyncLocalStorage();
|
||||
export const supportsRequestStorage = false;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
||||
|
||||
export const supportsComponentStorage = false;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
(null: any);
|
||||
|
||||
export * from '../ReactFlightServerConfigDebugNoop';
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-turbopack/src/ReactFlightServerConfigTurbopackBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
@@ -15,4 +16,8 @@ export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
export const supportsRequestStorage = false;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
||||
|
||||
export const supportsComponentStorage = false;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
(null: any);
|
||||
|
||||
export * from '../ReactFlightServerConfigDebugNoop';
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-webpack/src/ReactFlightServerConfigWebpackBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
@@ -15,4 +16,8 @@ export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
export const supportsRequestStorage = false;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
||||
|
||||
export const supportsComponentStorage = false;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
(null: any);
|
||||
|
||||
export * from '../ReactFlightServerConfigDebugNoop';
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from '../ReactFlightServerConfigBundlerCustom';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
@@ -15,4 +16,8 @@ export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
export const supportsRequestStorage = false;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
||||
|
||||
export const supportsComponentStorage = false;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
(null: any);
|
||||
|
||||
export * from '../ReactFlightServerConfigDebugNoop';
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-turbopack/src/ReactFlightServerConfigTurbopackBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
@@ -16,6 +17,11 @@ export const supportsRequestStorage = typeof AsyncLocalStorage === 'function';
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||
supportsRequestStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
export const supportsComponentStorage: boolean =
|
||||
__DEV__ && supportsRequestStorage;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
supportsComponentStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
// We use the Node version but get access to async_hooks from a global.
|
||||
import type {HookCallbacks, AsyncHook} from 'async_hooks';
|
||||
export const createAsyncHook: HookCallbacks => AsyncHook =
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-webpack/src/ReactFlightServerConfigWebpackBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
@@ -16,6 +18,11 @@ export const supportsRequestStorage = typeof AsyncLocalStorage === 'function';
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||
supportsRequestStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
export const supportsComponentStorage: boolean =
|
||||
__DEV__ && supportsRequestStorage;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
supportsComponentStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
// We use the Node version but get access to async_hooks from a global.
|
||||
import type {HookCallbacks, AsyncHook} from 'async_hooks';
|
||||
export const createAsyncHook: HookCallbacks => AsyncHook =
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from '../ReactFlightServerConfigBundlerCustom';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
@@ -15,4 +16,8 @@ export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
export const supportsRequestStorage = false;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
||||
|
||||
export const supportsComponentStorage = false;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
(null: any);
|
||||
|
||||
export * from '../ReactFlightServerConfigDebugNoop';
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import {AsyncLocalStorage} from 'async_hooks';
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-esm/src/ReactFlightServerConfigESMBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
@@ -17,5 +19,9 @@ export const supportsRequestStorage = true;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||
new AsyncLocalStorage();
|
||||
|
||||
export const supportsComponentStorage = __DEV__;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
supportsComponentStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
export {createHook as createAsyncHook, executionAsyncId} from 'async_hooks';
|
||||
export * from '../ReactFlightServerConfigDebugNode';
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import {AsyncLocalStorage} from 'async_hooks';
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-turbopack/src/ReactFlightServerConfigTurbopackBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
@@ -18,5 +19,9 @@ export const supportsRequestStorage = true;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||
new AsyncLocalStorage();
|
||||
|
||||
export const supportsComponentStorage = __DEV__;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
supportsComponentStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
export {createHook as createAsyncHook, executionAsyncId} from 'async_hooks';
|
||||
export * from '../ReactFlightServerConfigDebugNode';
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import {AsyncLocalStorage} from 'async_hooks';
|
||||
|
||||
import type {Request} from 'react-server/src/ReactFlightServer';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-server-dom-webpack/src/ReactFlightServerConfigWebpackBundler';
|
||||
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
|
||||
@@ -18,5 +19,9 @@ export const supportsRequestStorage = true;
|
||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||
new AsyncLocalStorage();
|
||||
|
||||
export const supportsComponentStorage = __DEV__;
|
||||
export const componentStorage: AsyncLocalStorage<ReactComponentInfo | void> =
|
||||
supportsComponentStorage ? new AsyncLocalStorage() : (null: any);
|
||||
|
||||
export {createHook as createAsyncHook, executionAsyncId} from 'async_hooks';
|
||||
export * from '../ReactFlightServerConfigDebugNode';
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {CacheDispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
|
||||
export function waitForSuspense<T>(fn: () => T): Promise<T> {
|
||||
const cache: Map<Function, mixed> = new Map();
|
||||
const testDispatcher: CacheDispatcher = {
|
||||
const testDispatcher: AsyncDispatcher = {
|
||||
getCacheForType<R>(resourceType: () => R): R {
|
||||
let entry: R | void = (cache.get(resourceType): any);
|
||||
if (entry === undefined) {
|
||||
@@ -22,12 +22,15 @@ export function waitForSuspense<T>(fn: () => T): Promise<T> {
|
||||
}
|
||||
return entry;
|
||||
},
|
||||
getOwner(): null {
|
||||
return null;
|
||||
},
|
||||
};
|
||||
// Not using async/await because we don't compile it.
|
||||
return new Promise((resolve, reject) => {
|
||||
function retry() {
|
||||
const prevDispatcher = ReactSharedInternals.C;
|
||||
ReactSharedInternals.C = testDispatcher;
|
||||
const prevDispatcher = ReactSharedInternals.A;
|
||||
ReactSharedInternals.A = testDispatcher;
|
||||
try {
|
||||
const result = fn();
|
||||
resolve(result);
|
||||
@@ -38,7 +41,7 @@ export function waitForSuspense<T>(fn: () => T): Promise<T> {
|
||||
reject(thrownValue);
|
||||
}
|
||||
} finally {
|
||||
ReactSharedInternals.C = prevDispatcher;
|
||||
ReactSharedInternals.A = prevDispatcher;
|
||||
}
|
||||
}
|
||||
retry();
|
||||
|
||||
@@ -54,7 +54,7 @@ function createCacheNode<T>(): CacheNode<T> {
|
||||
|
||||
export function cache<A: Iterable<mixed>, T>(fn: (...A) => T): (...A) => T {
|
||||
return function () {
|
||||
const dispatcher = ReactSharedInternals.C;
|
||||
const dispatcher = ReactSharedInternals.A;
|
||||
if (!dispatcher) {
|
||||
// If there is no dispatcher, then we treat this as not being cached.
|
||||
// $FlowFixMe[incompatible-call]: We don't want to use rest arguments since we transpile the code.
|
||||
|
||||
@@ -46,7 +46,7 @@ if (enableCache && enableFetchInstrumentation) {
|
||||
resource: URL | RequestInfo,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const dispatcher = ReactSharedInternals.C;
|
||||
const dispatcher = ReactSharedInternals.A;
|
||||
if (!dispatcher) {
|
||||
// We're outside a cached scope.
|
||||
return originalFetch(resource, options);
|
||||
|
||||
@@ -44,7 +44,7 @@ function resolveDispatcher() {
|
||||
}
|
||||
|
||||
export function getCacheForType<T>(resourceType: () => T): T {
|
||||
const dispatcher = ReactSharedInternals.C;
|
||||
const dispatcher = ReactSharedInternals.A;
|
||||
if (!dispatcher) {
|
||||
// If there is no dispatcher, then we treat this as not being cached.
|
||||
return resourceType();
|
||||
|
||||
@@ -8,19 +8,15 @@
|
||||
*/
|
||||
|
||||
import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {CacheDispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {BatchConfigTransition} from 'react-reconciler/src/ReactFiberTracingMarkerComponent';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
|
||||
import {disableStringRefs} from 'shared/ReactFeatureFlags';
|
||||
|
||||
export type SharedStateClient = {
|
||||
H: null | Dispatcher, // ReactCurrentDispatcher for Hooks
|
||||
C: null | CacheDispatcher, // ReactCurrentCache for Cache
|
||||
A: null | AsyncDispatcher, // ReactCurrentCache for Cache
|
||||
T: null | BatchConfigTransition, // ReactCurrentBatchConfig for Transitions
|
||||
|
||||
// DEV-only-ish
|
||||
owner: null | Fiber, // ReactCurrentOwner is Fiber on the Client, null in Fizz. Flight uses SharedStateServer.
|
||||
// DEV-only
|
||||
|
||||
// ReactCurrentActQueue
|
||||
actQueue: null | Array<RendererTask>,
|
||||
@@ -47,14 +43,10 @@ export type RendererTask = boolean => RendererTask | null;
|
||||
|
||||
const ReactSharedInternals: SharedStateClient = ({
|
||||
H: null,
|
||||
C: null,
|
||||
A: null,
|
||||
T: null,
|
||||
}: any);
|
||||
|
||||
if (__DEV__ || !disableStringRefs) {
|
||||
ReactSharedInternals.owner = null;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
ReactSharedInternals.actQueue = null;
|
||||
ReactSharedInternals.isBatchingLegacy = false;
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {CacheDispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
||||
import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
|
||||
|
||||
import type {
|
||||
Reference,
|
||||
@@ -24,11 +23,11 @@ import {
|
||||
TaintRegistryPendingRequests,
|
||||
} from './ReactTaintRegistry';
|
||||
|
||||
import {disableStringRefs, enableTaint} from 'shared/ReactFeatureFlags';
|
||||
import {enableTaint} from 'shared/ReactFeatureFlags';
|
||||
|
||||
export type SharedStateServer = {
|
||||
H: null | Dispatcher, // ReactCurrentDispatcher for Hooks
|
||||
C: null | CacheDispatcher, // ReactCurrentCache for Cache
|
||||
A: null | AsyncDispatcher, // ReactCurrentCache for Cache
|
||||
|
||||
// enableTaint
|
||||
TaintRegistryObjects: WeakMap<Reference, string>,
|
||||
@@ -36,8 +35,7 @@ export type SharedStateServer = {
|
||||
TaintRegistryByteLengths: Set<number>,
|
||||
TaintRegistryPendingRequests: Set<RequestCleanupQueue>,
|
||||
|
||||
// DEV-only-ish
|
||||
owner: null | ReactComponentInfo, // ReactCurrentOwner is ReactComponentInfo in Flight, null in Fizz. Fiber/Fizz uses SharedStateClient.
|
||||
// DEV-only
|
||||
|
||||
// ReactDebugCurrentFrame
|
||||
setExtraStackFrame: (stack: null | string) => void,
|
||||
@@ -49,7 +47,7 @@ export type RendererTask = boolean => RendererTask | null;
|
||||
|
||||
const ReactSharedInternals: SharedStateServer = ({
|
||||
H: null,
|
||||
C: null,
|
||||
A: null,
|
||||
}: any);
|
||||
|
||||
if (enableTaint) {
|
||||
@@ -60,10 +58,6 @@ if (enableTaint) {
|
||||
TaintRegistryPendingRequests;
|
||||
}
|
||||
|
||||
if (__DEV__ || !disableStringRefs) {
|
||||
ReactSharedInternals.owner = null;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
let currentExtraStackFrame = (null: null | string);
|
||||
ReactSharedInternals.setExtraStackFrame = function (stack: null | string) {
|
||||
|
||||
@@ -29,6 +29,17 @@ import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFrom
|
||||
|
||||
const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference');
|
||||
|
||||
function getOwner() {
|
||||
if (__DEV__ || !disableStringRefs) {
|
||||
const dispatcher = ReactSharedInternals.A;
|
||||
if (dispatcher === null) {
|
||||
return null;
|
||||
}
|
||||
return dispatcher.getOwner();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let specialPropKeyWarningShown;
|
||||
let specialPropRefWarningShown;
|
||||
let didWarnAboutStringRefs;
|
||||
@@ -66,16 +77,15 @@ function hasValidKey(config) {
|
||||
|
||||
function warnIfStringRefCannotBeAutoConverted(config, self) {
|
||||
if (__DEV__) {
|
||||
let owner;
|
||||
if (
|
||||
!disableStringRefs &&
|
||||
typeof config.ref === 'string' &&
|
||||
ReactSharedInternals.owner &&
|
||||
(owner = getOwner()) &&
|
||||
self &&
|
||||
ReactSharedInternals.owner.stateNode !== self
|
||||
owner.stateNode !== self
|
||||
) {
|
||||
const componentName = getComponentNameFromType(
|
||||
ReactSharedInternals.owner.type,
|
||||
);
|
||||
const componentName = getComponentNameFromType(owner.type);
|
||||
|
||||
if (!didWarnAboutStringRefs[componentName]) {
|
||||
console.error(
|
||||
@@ -85,7 +95,7 @@ function warnIfStringRefCannotBeAutoConverted(config, self) {
|
||||
'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: ' +
|
||||
'https://react.dev/link/strict-mode-string-ref',
|
||||
getComponentNameFromType(ReactSharedInternals.owner.type),
|
||||
getComponentNameFromType(owner.type),
|
||||
config.ref,
|
||||
);
|
||||
didWarnAboutStringRefs[componentName] = true;
|
||||
@@ -339,7 +349,7 @@ export function jsxProd(type, config, maybeKey) {
|
||||
if (!enableRefAsProp) {
|
||||
ref = config.ref;
|
||||
if (!disableStringRefs) {
|
||||
ref = coerceStringRef(ref, ReactSharedInternals.owner, type);
|
||||
ref = coerceStringRef(ref, getOwner(), type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -365,11 +375,7 @@ export function jsxProd(type, config, maybeKey) {
|
||||
// Skip over reserved prop names
|
||||
if (propName !== 'key' && (enableRefAsProp || propName !== 'ref')) {
|
||||
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
|
||||
props.ref = coerceStringRef(
|
||||
config[propName],
|
||||
ReactSharedInternals.owner,
|
||||
type,
|
||||
);
|
||||
props.ref = coerceStringRef(config[propName], getOwner(), type);
|
||||
} else {
|
||||
props[propName] = config[propName];
|
||||
}
|
||||
@@ -389,15 +395,7 @@ export function jsxProd(type, config, maybeKey) {
|
||||
}
|
||||
}
|
||||
|
||||
return ReactElement(
|
||||
type,
|
||||
key,
|
||||
ref,
|
||||
undefined,
|
||||
undefined,
|
||||
ReactSharedInternals.owner,
|
||||
props,
|
||||
);
|
||||
return ReactElement(type, key, ref, undefined, undefined, getOwner(), props);
|
||||
}
|
||||
|
||||
// While `jsxDEV` should never be called when running in production, we do
|
||||
@@ -571,7 +569,7 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
|
||||
if (!enableRefAsProp) {
|
||||
ref = config.ref;
|
||||
if (!disableStringRefs) {
|
||||
ref = coerceStringRef(ref, ReactSharedInternals.owner, type);
|
||||
ref = coerceStringRef(ref, getOwner(), type);
|
||||
}
|
||||
}
|
||||
if (!disableStringRefs) {
|
||||
@@ -600,11 +598,7 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
|
||||
// Skip over reserved prop names
|
||||
if (propName !== 'key' && (enableRefAsProp || propName !== 'ref')) {
|
||||
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
|
||||
props.ref = coerceStringRef(
|
||||
config[propName],
|
||||
ReactSharedInternals.owner,
|
||||
type,
|
||||
);
|
||||
props.ref = coerceStringRef(config[propName], getOwner(), type);
|
||||
} else {
|
||||
props[propName] = config[propName];
|
||||
}
|
||||
@@ -643,7 +637,7 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
|
||||
ref,
|
||||
self,
|
||||
source,
|
||||
ReactSharedInternals.owner,
|
||||
getOwner(),
|
||||
props,
|
||||
);
|
||||
|
||||
@@ -749,7 +743,7 @@ export function createElement(type, config, children) {
|
||||
if (!enableRefAsProp) {
|
||||
ref = config.ref;
|
||||
if (!disableStringRefs) {
|
||||
ref = coerceStringRef(ref, ReactSharedInternals.owner, type);
|
||||
ref = coerceStringRef(ref, getOwner(), type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,11 +773,7 @@ export function createElement(type, config, children) {
|
||||
propName !== '__source'
|
||||
) {
|
||||
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
|
||||
props.ref = coerceStringRef(
|
||||
config[propName],
|
||||
ReactSharedInternals.owner,
|
||||
type,
|
||||
);
|
||||
props.ref = coerceStringRef(config[propName], getOwner(), type);
|
||||
} else {
|
||||
props[propName] = config[propName];
|
||||
}
|
||||
@@ -839,7 +829,7 @@ export function createElement(type, config, children) {
|
||||
ref,
|
||||
undefined,
|
||||
undefined,
|
||||
ReactSharedInternals.owner,
|
||||
getOwner(),
|
||||
props,
|
||||
);
|
||||
|
||||
@@ -889,7 +879,7 @@ export function cloneElement(element, config, children) {
|
||||
|
||||
if (config != null) {
|
||||
if (hasValidRef(config)) {
|
||||
owner = ReactSharedInternals.owner;
|
||||
owner = __DEV__ || !disableStringRefs ? getOwner() : undefined;
|
||||
if (!enableRefAsProp) {
|
||||
// Silently steal the ref from the parent.
|
||||
ref = config.ref;
|
||||
@@ -983,8 +973,9 @@ export function cloneElement(element, config, children) {
|
||||
|
||||
function getDeclarationErrorAddendum() {
|
||||
if (__DEV__) {
|
||||
if (ReactSharedInternals.owner) {
|
||||
const name = getComponentNameFromType(ReactSharedInternals.owner.type);
|
||||
const owner = getOwner();
|
||||
if (owner) {
|
||||
const name = getComponentNameFromType(owner.type);
|
||||
if (name) {
|
||||
return '\n\nCheck the render method of `' + name + '`.';
|
||||
}
|
||||
@@ -1085,11 +1076,7 @@ function validateExplicitKey(element, parentType) {
|
||||
// property, it may be the creator of the child that's responsible for
|
||||
// assigning it a key.
|
||||
let childOwner = '';
|
||||
if (
|
||||
element &&
|
||||
element._owner != null &&
|
||||
element._owner !== ReactSharedInternals.owner
|
||||
) {
|
||||
if (element && element._owner != null && element._owner !== getOwner()) {
|
||||
let ownerName = null;
|
||||
if (typeof element._owner.tag === 'number') {
|
||||
ownerName = getComponentNameFromType(element._owner.type);
|
||||
|
||||
Reference in New Issue
Block a user