Skip to content

Commit

Permalink
Add test meant to show why it's tricky to merge both hydration levels
Browse files Browse the repository at this point in the history
Having both levels co-exist works. However, if we deprioritize hydration
using a single level, we might deprioritize the wrong thing.

This adds a test that catches it if we ever try a naive deprioritization
in the future.

It also tests that we don't down-prioritize if we're changing the hover
in the middle of doing continuous priority work.
  • Loading branch information
sebmarkbage committed Feb 1, 2020
1 parent 5910493 commit d92aea6
Showing 1 changed file with 108 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {createEventTarget} from 'dom-event-testing-library';
let React;
let ReactDOM;
let ReactDOMServer;
let ReactTestUtils;
let Scheduler;
let Suspense;
let usePress;
Expand Down Expand Up @@ -102,6 +103,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
React = require('react');
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');
Scheduler = require('scheduler');
Suspense = React.Suspense;
usePress = require('react-interactions/events/press').usePress;
Expand Down Expand Up @@ -817,4 +819,110 @@ describe('ReactDOMServerSelectiveHydration', () => {
// gets highest priority followed by the next added.
expect(Scheduler).toFlushAndYield(['App', 'C', 'B', 'A']);
});

it('hydrates before an update even if hydration moves away from it', async () => {
function Child({text}) {
Scheduler.unstable_yieldValue(text);
return <span>{text}</span>;
}
let ChildWithBoundary = React.memo(function({text}) {
return (
<Suspense fallback="Loading...">
<Child text={text} />
<Child text={text.toLowerCase()} />
</Suspense>
);
});

function App({a}) {
Scheduler.unstable_yieldValue('App');
React.useEffect(() => {
Scheduler.unstable_yieldValue('Commit');
});
return (
<div>
<ChildWithBoundary text={a} />
<ChildWithBoundary text="B" />
<ChildWithBoundary text="C" />
</div>
);
}

let finalHTML = ReactDOMServer.renderToString(<App a="A" />);

expect(Scheduler).toHaveYielded(['App', 'A', 'a', 'B', 'b', 'C', 'c']);

let container = document.createElement('div');
container.innerHTML = finalHTML;

// We need this to be in the document since we'll dispatch events on it.
document.body.appendChild(container);

let spanA = container.getElementsByTagName('span')[0];
let spanB = container.getElementsByTagName('span')[2];
let spanC = container.getElementsByTagName('span')[4];

let root = ReactDOM.createRoot(container, {hydrate: true});
ReactTestUtils.act(() => {
root.render(<App a="A" />);

// Hydrate the shell.
expect(Scheduler).toFlushAndYieldThrough(['App', 'Commit']);

// Render an update at Idle priority that needs to update A.
Scheduler.unstable_runWithPriority(
Scheduler.unstable_IdlePriority,
() => {
root.render(<App a="AA" />);
},
);

// Start rendering. This will force the first boundary to hydrate
// by scheduling it at one higher pri than Idle.
expect(Scheduler).toFlushAndYieldThrough(['App', 'A']);

// Hover over A which (could) schedule at one higher pri than Idle.
dispatchMouseHoverEvent(spanA, null);

// Before, we're done we now switch to hover over B.
// This is meant to test that this doesn't cause us to forget that
// we still have to hydrate A. The first boundary.
// This also tests that we don't do the -1 down-prioritization of
// continuous hover events because that would decrease its priority
// to Idle.
dispatchMouseHoverEvent(spanB, spanA);

// Also click C to prioritize that even higher which resets the
// priority levels.
dispatchClickEvent(spanC);

expect(Scheduler).toHaveYielded([
// Hydrate C first since we clicked it.
'C',
'c',
]);

expect(Scheduler).toFlushAndYield([
// Finish hydration of A since we forced it to hydrate.
'A',
'a',
// Also, hydrate B since we hovered over it.
// It's not important which one comes first. A or B.
// As long as they both happen before the Idle update.
'B',
'b',
// Begin the Idle update again.
'App',
'AA',
'aa',
'Commit',
]);
});

let spanA2 = container.getElementsByTagName('span')[0];
// This is supposed to have been hydrated, not replaced.
expect(spanA).toBe(spanA2);

document.body.removeChild(container);
});
});

0 comments on commit d92aea6

Please sign in to comment.