mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
LegacyHidden: mode that defers without hiding (#18958)
Need this to unblock www. Not sure yet how we'll support this properly long term. While adding this, I noticed that the normal "hidden" mode of LegacyHidden doesn't work properly because it doesn't toggle the visibility of newly inserted nodes. This is fine for now since we only use it via a userspace abstraction that wraps the children in an additional node. But implementing this correctly is required for us to start using it like a fragment, without the wrapper node.
This commit is contained in:
@@ -566,7 +566,10 @@ function updateOffscreenComponent(
|
||||
const prevState: OffscreenState | null =
|
||||
current !== null ? current.memoizedState : null;
|
||||
|
||||
if (nextProps.mode === 'hidden') {
|
||||
if (
|
||||
nextProps.mode === 'hidden' ||
|
||||
nextProps.mode === 'unstable-defer-without-hiding'
|
||||
) {
|
||||
if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
||||
// In legacy sync mode, don't defer the subtree. Render it now.
|
||||
// TODO: Figure out what we should do in Blocking mode.
|
||||
|
||||
@@ -1308,7 +1308,10 @@ function completeWork(
|
||||
|
||||
const prevIsHidden = prevState !== null;
|
||||
const nextIsHidden = nextState !== null;
|
||||
if (prevIsHidden !== nextIsHidden) {
|
||||
if (
|
||||
prevIsHidden !== nextIsHidden &&
|
||||
newProps.mode !== 'unstable-defer-without-hiding'
|
||||
) {
|
||||
workInProgress.effectTag |= Update;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export type OffscreenProps = {|
|
||||
//
|
||||
// Default mode is visible. Kind of a weird default for a component
|
||||
// called "Offscreen." Possible alt: <Visibility />?
|
||||
mode?: 'hidden' | 'visible' | null | void,
|
||||
mode?: 'hidden' | 'unstable-defer-without-hiding' | 'visible' | null | void,
|
||||
children?: ReactNodeList,
|
||||
|};
|
||||
|
||||
|
||||
80
packages/react-reconciler/src/__tests__/ReactOffscreen-test.js
vendored
Normal file
80
packages/react-reconciler/src/__tests__/ReactOffscreen-test.js
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
let React;
|
||||
let ReactNoop;
|
||||
let Scheduler;
|
||||
let LegacyHidden;
|
||||
|
||||
describe('ReactOffscreen', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
React = require('react');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
Scheduler = require('scheduler');
|
||||
LegacyHidden = React.unstable_LegacyHidden;
|
||||
});
|
||||
|
||||
function Text(props) {
|
||||
Scheduler.unstable_yieldValue(props.text);
|
||||
return <span prop={props.text} />;
|
||||
}
|
||||
|
||||
// @gate experimental
|
||||
// @gate new
|
||||
it('unstable-defer-without-hiding should never toggle the visibility of its children', async () => {
|
||||
function App({mode}) {
|
||||
return (
|
||||
<>
|
||||
<Text text="Normal" />
|
||||
<LegacyHidden mode={mode}>
|
||||
<Text text="Deferred" />
|
||||
</LegacyHidden>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Test the initial mount
|
||||
const root = ReactNoop.createRoot();
|
||||
await ReactNoop.act(async () => {
|
||||
root.render(<App mode="unstable-defer-without-hiding" />);
|
||||
expect(Scheduler).toFlushUntilNextPaint(['Normal']);
|
||||
expect(root).toMatchRenderedOutput(<span prop="Normal" />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Deferred']);
|
||||
expect(root).toMatchRenderedOutput(
|
||||
<>
|
||||
<span prop="Normal" />
|
||||
<span prop="Deferred" />
|
||||
</>,
|
||||
);
|
||||
|
||||
// Now try after an update
|
||||
await ReactNoop.act(async () => {
|
||||
root.render(<App mode="visible" />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Normal', 'Deferred']);
|
||||
expect(root).toMatchRenderedOutput(
|
||||
<>
|
||||
<span prop="Normal" />
|
||||
<span prop="Deferred" />
|
||||
</>,
|
||||
);
|
||||
|
||||
await ReactNoop.act(async () => {
|
||||
root.render(<App mode="unstable-defer-without-hiding" />);
|
||||
expect(Scheduler).toFlushUntilNextPaint(['Normal']);
|
||||
expect(root).toMatchRenderedOutput(
|
||||
<>
|
||||
<span prop="Normal" />
|
||||
<span prop="Deferred" />
|
||||
</>,
|
||||
);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Deferred']);
|
||||
expect(root).toMatchRenderedOutput(
|
||||
<>
|
||||
<span prop="Normal" />
|
||||
<span prop="Deferred" />
|
||||
</>,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user