Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 38 additions & 16 deletions packages/react-client/src/ReactFlightClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -1280,10 +1280,11 @@ function getTaskName(type: mixed): string {
type !== null &&
type.$$typeof === REACT_LAZY_TYPE
) {
if (type._init === readChunk) {
// This is a lazy node created by Flight. It is probably a client reference.
// We use the "use client" string to indicate that this is the boundary into
// the client. There will only be one for any given owner chain.
if (type._payload instanceof ReactPromise) {
// This is a lazy node created by Flight, i.e. it wraps a chunk. It is
// probably a client reference. We use the "use client" string to indicate
// that this is the boundary into the client. There will only be one for
// any given owner chain.
return '"use client"';
}
// We don't want to eagerly initialize the initializer in DEV mode so we can't
Expand Down Expand Up @@ -1374,16 +1375,6 @@ function initializeElement(
}

if (lazyNode !== null) {
// In case the JSX runtime has validated the lazy type as a static child, we
// need to transfer this information to the element.
if (
lazyNode._store &&
lazyNode._store.validated &&
!element._store.validated
) {
element._store.validated = lazyNode._store.validated;
}

// If the lazy node is initialized, we move its debug info to the inner
// value.
if (lazyNode._payload.status === INITIALIZED && lazyNode._debugInfo) {
Expand Down Expand Up @@ -1535,6 +1526,29 @@ function createElement(
return element;
}

function transferValidation(store: {validated: 0 | 1 | 2}, value: mixed): void {
if (store.validated && typeof value === 'object' && value !== null) {
// Only elements and lazy nodes carry key validation. Any other value, e.g.
// an array of children, needs to have its own items validated instead.
const $$typeof = (value as any).$$typeof;
if ($$typeof === REACT_ELEMENT_TYPE || $$typeof === REACT_LAZY_TYPE) {
const valueStore = (value as any)._store;
if (valueStore && !valueStore.validated) {
valueStore.validated = store.validated;
}
}
}
}

function readChunkAndTransferValidation<T>(
store: {validated: 0 | 1 | 2},
payload: SomeChunk<T>,
): T {
const value: T = readChunk(payload);
transferValidation(store, value);
return value;
}

function createLazyChunkWrapper<T>(
chunk: SomeChunk<T>,
validated: 0 | 1 | 2, // DEV-only
Expand All @@ -1547,8 +1561,16 @@ function createLazyChunkWrapper<T>(
if (__DEV__) {
// Forward the live array
lazyType._debugInfo = chunk._debugInfo;
// Initialize a store for key validation by the JSX runtime.
lazyType._store = {validated: validated};
// Initialize a store for key validation by the JSX runtime. It can only
// validate the lazy node itself, because the value it refers to might not
// exist yet at that point, e.g. if it's an outlined row that hasn't been
// initialized. So the validation is transferred to the value when the lazy
// node is unwrapped. If the value is another lazy node, unwrapping that one
// forwards the validation further.
const store = {validated: validated};
lazyType._store = store;
// $FlowFixMe[incompatible-type] `bind` loses the type argument.
lazyType._init = readChunkAndTransferValidation.bind(null, store);
}
return lazyType;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/react-debug-tools/src/ReactDebugHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
$$typeof: REACT_CONTEXT_TYPE,
_currentValue: null,
} as any);
const recoverable = new Error();
Object.defineProperty(recoverable as any, '$$typeof', {
value: REACT_RECOVERABLE_TYPE,
});
const recoverable = {
$$typeof: REACT_RECOVERABLE_TYPE,
_reason: undefined,
};
Dispatcher.use(recoverable as any);
Dispatcher.use({
then() {},
Expand Down
6 changes: 5 additions & 1 deletion packages/react-dom/src/__tests__/ReactDOMBrowser-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ describe('ReactDOM.browser', () => {
it('can create browser-only content before the browser renderer is initialized', async () => {
const React = require('react');
const ReactDOM = require('react-dom');
const browserOnly = ReactDOM.browser();
const initializeReason = jest.fn(
() => new Error('Only render this content in a browser'),
);
const browserOnly = ReactDOM.browser(initializeReason);
const ReactDOMClient = require('react-dom/client');
const {act} = require('internal-test-utils');

Expand All @@ -37,5 +40,6 @@ describe('ReactDOM.browser', () => {
);
});
expect(container.innerHTML).toBe('<span>Browser</span>');
expect(initializeReason).not.toHaveBeenCalled();
});
});
Loading
Loading