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
7 changes: 6 additions & 1 deletion fixtures/flight/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import {renderToReadableStream} from 'react-server-dom-unbundled/server';
import {createFromReadableStream} from 'react-server-dom-webpack/client';
import {PassThrough, Readable} from 'stream';

import {ClientContext, ClientReadContext} from './ClientContext.js';
import Container from './Container.js';

import {Counter} from './Counter.js';
Expand Down Expand Up @@ -235,6 +235,11 @@ export default async function App({prerender, noCache}) {
<Foo>{dedupedChild}</Foo>
<Bar>{Promise.resolve([dedupedChild])}</Bar>
<Navigate />
<ClientContext value="from server">
<div>
<ClientReadContext />
</div>
</ClientContext>
{prerender ? null : ( // TODO: prerender is broken for large content for some reason.
<React.Suspense fallback={null}>
<LargeContent />
Expand Down
12 changes: 12 additions & 0 deletions fixtures/flight/src/ClientContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use client';

import {createContext, use} from 'react';

const ClientContext = createContext(null);

function ClientReadContext() {
const value = use(ClientContext);
return <p>{value}</p>;
}

export {ClientContext, ClientReadContext};
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,84 @@ describe('ProfilerContext', () => {
document.body.removeChild(profilerContainer);
});

it('should reset commit index when switching to a different root', async () => {
const Parent = () => <Child />;
const Child = () => null;

const containerA = document.createElement('div');
const containerB = document.createElement('div');

const rootA = ReactDOMClient.createRoot(containerA);
const rootB = ReactDOMClient.createRoot(containerB);

utils.act(() => rootA.render(<Parent />));
utils.act(() => rootB.render(<Parent />));

// Profile and record different numbers of commits for each root
// Root A: 5 commits, Root B: 2 commits
await utils.actAsync(() => store.profilerStore.startProfiling());
await utils.actAsync(() => rootA.render(<Parent />)); // Root A commit 1
await utils.actAsync(() => rootA.render(<Parent />)); // Root A commit 2
await utils.actAsync(() => rootA.render(<Parent />)); // Root A commit 3
await utils.actAsync(() => rootA.render(<Parent />)); // Root A commit 4
await utils.actAsync(() => rootA.render(<Parent />)); // Root A commit 5
await utils.actAsync(() => rootB.render(<Parent />)); // Root B commit 1
await utils.actAsync(() => rootB.render(<Parent />)); // Root B commit 2
await utils.actAsync(() => store.profilerStore.stopProfiling());

let context: Context = ((null: any): Context);
function ContextReader() {
context = React.useContext(ProfilerContext);
return null;
}

await utils.actAsync(() =>
TestRenderer.create(
<Contexts>
<ContextReader />
</Contexts>,
),
);

// Verify we have profiling data for both roots
expect(context.didRecordCommits).toBe(true);
expect(context.profilingData).not.toBeNull();

const rootIDs = Array.from(context.profilingData.dataForRoots.keys());
expect(rootIDs.length).toBe(2);

const [rootAID, rootBID] = rootIDs;
const rootAData = context.profilingData.dataForRoots.get(rootAID);
const rootBData = context.profilingData.dataForRoots.get(rootBID);

expect(rootAData.commitData.length).toBe(5);
expect(rootBData.commitData.length).toBe(2);

// Select root A and navigate to commit 4 (index 3)
await utils.actAsync(() => context.setRootID(rootAID));
expect(context.rootID).toBe(rootAID);
expect(context.selectedCommitIndex).toBe(0);

await utils.actAsync(() => context.selectCommitIndex(3));
expect(context.selectedCommitIndex).toBe(3);

// Switch to root B which only has 2 commits
// The commit index should be reset to 0, not stay at 3 (which would be invalid)
await utils.actAsync(() => context.setRootID(rootBID));
expect(context.rootID).toBe(rootBID);
// Should be reset to 0 since commit 3 doesn't exist in root B
expect(context.selectedCommitIndex).toBe(0);

// Verify we can still navigate in root B
await utils.actAsync(() => context.selectCommitIndex(1));
expect(context.selectedCommitIndex).toBe(1);

// Switch back to root A - should reset to 0
await utils.actAsync(() => context.setRootID(rootAID));
expect(context.rootID).toBe(rootAID);
expect(context.selectedCommitIndex).toBe(0);
});

it('should handle commit selection edge cases when filtering commits', async () => {
const Scheduler = require('scheduler');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ export function useCommitFilteringAndNavigation(
null,
);

// Reset commit index when commitData changes (e.g., when switching roots).
const [previousCommitData, setPreviousCommitData] =
useState<Array<CommitDataFrontend>>(commitData);
if (previousCommitData !== commitData) {
setPreviousCommitData(commitData);
selectCommitIndex(commitData.length > 0 ? 0 : null);
}

const calculateFilteredIndices = useCallback(
(enabled: boolean, minDuration: number): Array<number> => {
return commitData.reduce((reduced: Array<number>, commitDatum, index) => {
Expand Down
7 changes: 0 additions & 7 deletions packages/react-dom/src/__tests__/ReactDOMActivity-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ describe('ReactDOMActivity', () => {
return <span prop={props.text}>{props.children}</span>;
}

// @gate enableActivity
it(
'hiding an Activity boundary also hides the direct children of any ' +
'portals it contains, regardless of how deeply nested they are',
Expand Down Expand Up @@ -100,7 +99,6 @@ describe('ReactDOMActivity', () => {
},
);

// @gate enableActivity
it(
'revealing an Activity boundary inside a portal does not reveal the ' +
'portal contents if has a hidden Activity parent',
Expand Down Expand Up @@ -151,7 +149,6 @@ describe('ReactDOMActivity', () => {
},
);

// @gate enableActivity
it('hides new portals added to an already hidden tree', async () => {
function Child() {
return <Text text="Child" />;
Expand Down Expand Up @@ -218,7 +215,6 @@ describe('ReactDOMActivity', () => {
);
});

// @gate enableActivity
it('hides new insertions inside an already hidden portal', async () => {
function Child({text}) {
useLayoutEffect(() => {
Expand Down Expand Up @@ -289,7 +285,6 @@ describe('ReactDOMActivity', () => {
);
});

// @gate enableActivity
it('reveal an inner Suspense boundary without revealing an outer Activity on the same host child', async () => {
const promise = new Promise(() => {});

Expand Down Expand Up @@ -390,7 +385,6 @@ describe('ReactDOMActivity', () => {
);
});

// @gate enableActivity
it('mounts/unmounts layout effects in portal when visibility changes (starting visible)', async () => {
function Child() {
useLayoutEffect(() => {
Expand Down Expand Up @@ -440,7 +434,6 @@ describe('ReactDOMActivity', () => {
);
});

// @gate enableActivity
it('mounts/unmounts layout effects in portal when visibility changes (starting hidden)', async () => {
function Child() {
useLayoutEffect(() => {
Expand Down
6 changes: 3 additions & 3 deletions packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ describe('FragmentRefs', () => {
});

describe('with activity', () => {
// @gate enableFragmentRefs && enableActivity
// @gate enableFragmentRefs
it('does not apply event listeners to hidden trees', async () => {
const parentRef = React.createRef();
const fragmentRef = React.createRef();
Expand Down Expand Up @@ -799,7 +799,7 @@ describe('FragmentRefs', () => {
expect(logs).toEqual(['Child 1', 'Child 3']);
});

// @gate enableFragmentRefs && enableActivity
// @gate enableFragmentRefs
it('applies event listeners to visible trees', async () => {
const parentRef = React.createRef();
const fragmentRef = React.createRef();
Expand Down Expand Up @@ -835,7 +835,7 @@ describe('FragmentRefs', () => {
expect(logs).toEqual(['Child 1', 'Child 2', 'Child 3']);
});

// @gate enableFragmentRefs && enableActivity
// @gate enableFragmentRefs
it('handles Activity modes switching', async () => {
const fragmentRef = React.createRef();
const fragmentRef2 = React.createRef();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3668,7 +3668,6 @@ describe('ReactDOMServerPartialHydration', () => {
expect(ref.current.innerHTML).toBe('Hidden child');
});

// @gate enableActivity
it('a visible Activity component is surrounded by comment markers', async () => {
const ref = React.createRef();

Expand Down Expand Up @@ -3706,7 +3705,6 @@ describe('ReactDOMServerPartialHydration', () => {
expect(ref.current).toBe(span);
});

// @gate enableActivity
it('a hidden Activity component is skipped over during server rendering', async () => {
const visibleRef = React.createRef();

Expand Down
Loading
Loading