Skip to content

Commit

Permalink
Add LegacyHidden to server renderer (facebook#18919)
Browse files Browse the repository at this point in the history
* Add LegacyHidden to server renderer

When the tree is hidden, the server renderer renders nothing. The
contents will be completely client rendered.

When the tree is visible it acts like a fragment.

The future streaming server renderer may want to pre-render these trees
and send them down in chunks, as with Suspense boundaries.

* Force client render, even at Offscreen pri
  • Loading branch information
acdlite committed May 14, 2020
1 parent b4a1a49 commit 0fb747f
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -2809,4 +2809,93 @@ describe('ReactDOMServerPartialHydration', () => {
// Now we're hydrated.
expect(ref.current).not.toBe(null);
});

// @gate experimental
// @gate new
it('renders a hidden LegacyHidden component', async () => {
const LegacyHidden = React.unstable_LegacyHidden;

const ref = React.createRef();

function App() {
return (
<LegacyHidden mode="hidden">
<span ref={ref}>Hidden child</span>
</LegacyHidden>
);
}

const finalHTML = ReactDOMServer.renderToString(<App />);

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

const span = container.getElementsByTagName('span')[0];
expect(span).toBe(undefined);

const root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
expect(ref.current.innerHTML).toBe('Hidden child');
});

// @gate experimental
// @gate new
it('renders a hidden LegacyHidden component inside a Suspense boundary', async () => {
const LegacyHidden = React.unstable_LegacyHidden;

const ref = React.createRef();

function App() {
return (
<Suspense fallback="Loading...">
<LegacyHidden mode="hidden">
<span ref={ref}>Hidden child</span>
</LegacyHidden>
</Suspense>
);
}

const finalHTML = ReactDOMServer.renderToString(<App />);

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

const span = container.getElementsByTagName('span')[0];
expect(span).toBe(undefined);

const root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
expect(ref.current.innerHTML).toBe('Hidden child');
});

// @gate experimental
// @gate new
it('renders a visible LegacyHidden component', async () => {
const LegacyHidden = React.unstable_LegacyHidden;

const ref = React.createRef();

function App() {
return (
<LegacyHidden mode="visible">
<span ref={ref}>Hidden child</span>
</LegacyHidden>
);
}

const finalHTML = ReactDOMServer.renderToString(<App />);

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

const span = container.getElementsByTagName('span')[0];

const root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
expect(ref.current).toBe(span);
expect(ref.current.innerHTML).toBe('Hidden child');
});
});
13 changes: 13 additions & 0 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
REACT_MEMO_TYPE,
REACT_FUNDAMENTAL_TYPE,
REACT_SCOPE_TYPE,
REACT_LEGACY_HIDDEN_TYPE,
} from 'shared/ReactSymbols';

import {
Expand Down Expand Up @@ -1019,6 +1020,18 @@ class ReactDOMServerRenderer {
}

switch (elementType) {
case REACT_LEGACY_HIDDEN_TYPE: {
if (!enableSuspenseServerRenderer) {
break;
}
if (((nextChild: any): ReactElement).props.mode === 'hidden') {
// In hidden mode, render nothing.
return '';
}
// Otherwise the tree is visible, so act like a fragment.
}
// Intentional fall through
// eslint-disable-next-line no-fallthrough
case REACT_DEBUG_TRACING_MODE_TYPE:
case REACT_STRICT_MODE_TYPE:
case REACT_PROFILER_TYPE:
Expand Down
9 changes: 8 additions & 1 deletion packages/react-reconciler/src/ReactFiberBeginWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ import {
reenterHydrationStateFromDehydratedSuspenseInstance,
resetHydrationState,
tryToClaimNextHydratableInstance,
getIsHydrating,
warnIfHydrating,
} from './ReactFiberHydrationContext.new';
import {
Expand Down Expand Up @@ -574,7 +575,13 @@ function updateOffscreenComponent(
};
workInProgress.memoizedState = nextState;
pushRenderLanes(workInProgress, renderLanes);
} else if (!includesSomeLane(renderLanes, (OffscreenLane: Lane))) {
} else if (
!includesSomeLane(renderLanes, (OffscreenLane: Lane)) ||
// Server renderer does not render hidden subtrees, so if we're hydrating
// we should always bail out and schedule a subsequent render pass, to
// force a client render. Even if we're already at Offscreen priority.
(current === null && getIsHydrating())
) {
let nextBaseLanes;
if (prevState !== null) {
const prevBaseLanes = prevState.baseLanes;
Expand Down

0 comments on commit 0fb747f

Please sign in to comment.