Skip to content

Commit

Permalink
[Fizz] Replay Postponed Paths (facebook#27379)
Browse files Browse the repository at this point in the history
This forks Task into ReplayTask and RenderTask.

A RenderTask is the normal mode and it has a segment to write into.

A ReplayTask doesn't have a segment to write into because that has
already been written but instead it has a ReplayState which keeps track
of the next set of paths to follow. Once we hit a "Resume" node we
convert it into a RenderTask and continue rendering from there.

We can resume at either an Element position or a Slot position. An
Element pointing to a component doesn't mean we resume that component,
it means we resume in the child position directly below that component.
Slots are slots inside arrays.

Instead of statically forking most paths, I kept using the same path and
checked for the existence of a segment or replay state dynamically at
runtime.

However, there's still quite a bit of forking here like retryRenderTask
and retryReplayTask. Even in the new forks there's a lot of duplication
like resumeSuspenseBoundary, replaySuspenseBoundary and
renderSuspenseBoundary. There's opportunity to simplify this a bit.
  • Loading branch information
sebmarkbage authored and AndyPengc12 committed Apr 15, 2024
1 parent b0c1f95 commit bd6d3db
Show file tree
Hide file tree
Showing 4 changed files with 1,211 additions and 219 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6365,6 +6365,6 @@ describe('ReactDOMFizzServer', () => {
resumed.pipe(writable);
});

// TODO: expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
});
});
137 changes: 133 additions & 4 deletions packages/react-dom/src/__tests__/ReactDOMFizzStaticBrowser-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ describe('ReactDOMFizzStaticBrowser', () => {
}
const temp = document.createElement('div');
temp.innerHTML = result;
insertNodesAndExecuteScripts(temp, container, null);
await insertNodesAndExecuteScripts(temp, container, null);
}

// @gate experimental
Expand Down Expand Up @@ -490,7 +490,51 @@ describe('ReactDOMFizzStaticBrowser', () => {

await readIntoContainer(resumed);

// TODO: expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
expect(getVisibleChildren(container)).toEqual(
<div>{['Hello', 'World']}</div>,
);
});

// @gate enablePostpone
it('supports postponing in prerender and resuming with a prefix', async () => {
let prerendering = true;
function Postpone() {
if (prerendering) {
React.unstable_postpone();
}
return 'World';
}

function App() {
return (
<div>
<Suspense fallback="Loading...">
Hello
<Postpone />
</Suspense>
</div>
);
}

const prerendered = await ReactDOMFizzStatic.prerender(<App />);
expect(prerendered.postponed).not.toBe(null);

prerendering = false;

const resumed = await ReactDOMFizzServer.resume(
<App />,
prerendered.postponed,
);

await readIntoContainer(prerendered.prelude);

expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);

await readIntoContainer(resumed);

expect(getVisibleChildren(container)).toEqual(
<div>{['Hello', 'World']}</div>,
);
});

// @gate enablePostpone
Expand All @@ -500,7 +544,51 @@ describe('ReactDOMFizzStaticBrowser', () => {
React.unstable_postpone();
});

function App() {
return (
<div>
<Suspense fallback="Loading...">
Hi
{prerendering ? Hole : 'Hello'}
</Suspense>
</div>
);
}

const prerendered = await ReactDOMFizzStatic.prerender(<App />);
expect(prerendered.postponed).not.toBe(null);

prerendering = false;

const resumed = await ReactDOMFizzServer.resume(
<App />,
prerendered.postponed,
);

await readIntoContainer(prerendered.prelude);

expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);

await readIntoContainer(resumed);

expect(getVisibleChildren(container)).toEqual(
<div>
{'Hi'}
{'Hello'}
</div>,
);
});

// @gate enablePostpone
it('supports postponing in a nested array', async () => {
let prerendering = true;
const Hole = React.lazy(async () => {
React.unstable_postpone();
});
function Postpone() {
if (prerendering) {
React.unstable_postpone();
}
return 'Hello';
}

Expand All @@ -509,7 +597,48 @@ describe('ReactDOMFizzStaticBrowser', () => {
<div>
<Suspense fallback="Loading...">
Hi
{prerendering ? Hole : <Postpone />}
{[<Postpone key="key" />, prerendering ? Hole : 'World']}
</Suspense>
</div>
);
}

const prerendered = await ReactDOMFizzStatic.prerender(<App />);
expect(prerendered.postponed).not.toBe(null);

prerendering = false;

const resumed = await ReactDOMFizzServer.resume(
<App />,
prerendered.postponed,
);

await readIntoContainer(prerendered.prelude);

expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);

await readIntoContainer(resumed);

expect(getVisibleChildren(container)).toEqual(
<div>{['Hi', 'Hello', 'World']}</div>,
);
});

// @gate enablePostpone
it('supports postponing in lazy as a direct child', async () => {
let prerendering = true;
const Hole = React.lazy(async () => {
React.unstable_postpone();
});
function Postpone() {
return prerendering ? Hole : 'Hello';
}

function App() {
return (
<div>
<Suspense fallback="Loading...">
<Postpone key="key" />
</Suspense>
</div>
);
Expand All @@ -531,7 +660,7 @@ describe('ReactDOMFizzStaticBrowser', () => {

await readIntoContainer(resumed);

// TODO: expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
});

// @gate enablePostpone
Expand Down
Loading

0 comments on commit bd6d3db

Please sign in to comment.