Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8b9c4d1

Browse files
authored
Expose LegacyHidden type and disable <div hidden /> API in new fork (facebook#18891)
* Expose LegacyHidden type I will use this internally at Facebook to migrate away from <div hidden />. The end goal is to migrate to the Offscreen type, but that has different semantics. This is an incremental step. * Disable <div hidden /> API in new fork Migrates to the unstable_LegacyHidden type instead. The old fork does not support the new component type, so I updated the tests to use an indirection that picks the correct API. I will remove this once the LegacyHidden (and/or Offscreen) type has landed in both implementations. * Add gated warning for `<div hidden />` API Only exists so we can detect callers in www and migrate them to the new API. Should not visible to anyone outside React Core team.
1 parent ef0bf8e commit 8b9c4d1

27 files changed

+303
-69
lines changed

packages/react-dom/src/__tests__/ReactUpdates-test.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@ describe('ReactUpdates', () => {
2525
Scheduler = require('scheduler');
2626
});
2727

28+
// TODO: Delete this once new API exists in both forks
29+
function LegacyHiddenDiv({hidden, children, ...props}) {
30+
if (gate(flags => flags.new)) {
31+
return (
32+
<div hidden={hidden} {...props}>
33+
<React.unstable_LegacyHidden mode={hidden ? 'hidden' : 'visible'}>
34+
{children}
35+
</React.unstable_LegacyHidden>
36+
</div>
37+
);
38+
} else {
39+
return (
40+
<div hidden={hidden} {...props}>
41+
{children}
42+
</div>
43+
);
44+
}
45+
}
46+
2847
it('should batch state when updating state twice', () => {
2948
let updateCount = 0;
3049

@@ -1288,6 +1307,7 @@ describe('ReactUpdates', () => {
12881307
});
12891308

12901309
// @gate experimental
1310+
// @gate enableLegacyHiddenType
12911311
it('delays sync updates inside hidden subtrees in Concurrent Mode', () => {
12921312
const container = document.createElement('div');
12931313

@@ -1311,9 +1331,9 @@ describe('ReactUpdates', () => {
13111331
});
13121332
return (
13131333
<div>
1314-
<div hidden={true}>
1334+
<LegacyHiddenDiv hidden={true}>
13151335
<Bar />
1316-
</div>
1336+
</LegacyHiddenDiv>
13171337
<Baz />
13181338
</div>
13191339
);

packages/react-reconciler/src/ReactFiberBeginWork.new.js

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,13 @@ import {
7575
warnAboutDefaultPropsOnFunctionComponents,
7676
enableScopeAPI,
7777
enableBlocksAPI,
78+
warnAboutDOMHiddenAttribute,
7879
} from 'shared/ReactFeatureFlags';
7980
import invariant from 'shared/invariant';
8081
import shallowEqual from 'shared/shallowEqual';
8182
import getComponentName from 'shared/getComponentName';
8283
import ReactStrictModeWarnings from './ReactStrictModeWarnings.new';
83-
import {
84-
REACT_ELEMENT_TYPE,
85-
REACT_LAZY_TYPE,
86-
REACT_LEGACY_HIDDEN_TYPE,
87-
getIteratorFn,
88-
} from 'shared/ReactSymbols';
84+
import {REACT_LAZY_TYPE, getIteratorFn} from 'shared/ReactSymbols';
8985
import {
9086
getCurrentFiberOwnerNameInDevOrNull,
9187
setIsRendering,
@@ -128,7 +124,6 @@ import {
128124
} from './ReactTypeOfMode';
129125
import {
130126
shouldSetTextContent,
131-
shouldDeprioritizeSubtree,
132127
isSuspenseInstancePending,
133128
isSuspenseInstanceFallback,
134129
registerSuspenseInstanceRetry,
@@ -572,7 +567,15 @@ function updateOffscreenComponent(
572567
current !== null ? current.memoizedState : null;
573568

574569
if (nextProps.mode === 'hidden') {
575-
if (!includesSomeLane(renderLanes, (OffscreenLane: Lane))) {
570+
if ((workInProgress.mode & ConcurrentMode) === NoMode) {
571+
// In legacy sync mode, don't defer the subtree. Render it now.
572+
// TODO: Figure out what we should do in Blocking mode.
573+
const nextState: OffscreenState = {
574+
baseLanes: NoLanes,
575+
};
576+
workInProgress.memoizedState = nextState;
577+
pushRenderLanes(workInProgress, renderLanes);
578+
} else if (!includesSomeLane(renderLanes, (OffscreenLane: Lane))) {
576579
let nextBaseLanes;
577580
if (prevState !== null) {
578581
const prevBaseLanes = prevState.baseLanes;
@@ -1122,23 +1125,19 @@ function updateHostComponent(
11221125

11231126
markRef(current, workInProgress);
11241127

1125-
if (
1126-
(workInProgress.mode & ConcurrentMode) !== NoMode &&
1127-
nextProps.hasOwnProperty('hidden')
1128-
) {
1129-
const wrappedChildren = {
1130-
$$typeof: REACT_ELEMENT_TYPE,
1131-
type: REACT_LEGACY_HIDDEN_TYPE,
1132-
key: null,
1133-
ref: null,
1134-
props: {
1135-
children: nextChildren,
1136-
// Check the host config to see if the children are offscreen/hidden.
1137-
mode: shouldDeprioritizeSubtree(type, nextProps) ? 'hidden' : 'visible',
1138-
},
1139-
_owner: __DEV__ ? {} : null,
1140-
};
1141-
nextChildren = wrappedChildren;
1128+
if (__DEV__) {
1129+
if (
1130+
warnAboutDOMHiddenAttribute &&
1131+
(workInProgress.mode & ConcurrentMode) !== NoMode &&
1132+
nextProps.hasOwnProperty('hidden')
1133+
) {
1134+
// This warning will not be user visible. Only exists so React Core team
1135+
// can find existing callers and migrate them to the new API.
1136+
console.error(
1137+
'Detected use of DOM `hidden` attribute. Should migrate to new API. ' +
1138+
'(owner: React Core)',
1139+
);
1140+
}
11421141
}
11431142

11441143
reconcileChildren(current, workInProgress, nextChildren, renderLanes);

packages/react-reconciler/src/ReactFiberBeginWork.old.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import {
6969
warnAboutDefaultPropsOnFunctionComponents,
7070
enableScopeAPI,
7171
enableBlocksAPI,
72+
warnAboutDOMHiddenAttribute,
7273
} from 'shared/ReactFeatureFlags';
7374
import invariant from 'shared/invariant';
7475
import shallowEqual from 'shared/shallowEqual';
@@ -1099,6 +1100,21 @@ function updateHostComponent(current, workInProgress, renderExpirationTime) {
10991100

11001101
markRef(current, workInProgress);
11011102

1103+
if (__DEV__) {
1104+
if (
1105+
warnAboutDOMHiddenAttribute &&
1106+
(workInProgress.mode & ConcurrentMode) !== NoMode &&
1107+
nextProps.hasOwnProperty('hidden')
1108+
) {
1109+
// This warning will not be user visible. Only exists so React Core team
1110+
// can find existing callers and migrate them to the new API.
1111+
console.error(
1112+
'Detected use of DOM `hidden` attribute. Should migrate to new API. ' +
1113+
'(owner: React Core)',
1114+
);
1115+
}
1116+
}
1117+
11021118
// Check the host config to see if the children are offscreen/hidden.
11031119
if (
11041120
workInProgress.mode & ConcurrentMode &&

packages/react-reconciler/src/__tests__/ReactIncremental-test.js

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,25 @@ describe('ReactIncremental', () => {
2424
PropTypes = require('prop-types');
2525
});
2626

27+
// TODO: Delete this once new API exists in both forks
28+
function LegacyHiddenDiv({hidden, children, ...props}) {
29+
if (gate(flags => flags.new)) {
30+
return (
31+
<div hidden={hidden} {...props}>
32+
<React.unstable_LegacyHidden mode={hidden ? 'hidden' : 'visible'}>
33+
{children}
34+
</React.unstable_LegacyHidden>
35+
</div>
36+
);
37+
} else {
38+
return (
39+
<div hidden={hidden} {...props}>
40+
{children}
41+
</div>
42+
);
43+
}
44+
}
45+
2746
it('should render a simple component', () => {
2847
function Bar() {
2948
return <div>Hello World</div>;
@@ -210,6 +229,7 @@ describe('ReactIncremental', () => {
210229
expect(inst.state).toEqual({text: 'bar', text2: 'baz'});
211230
});
212231

232+
// @gate enableLegacyHiddenType
213233
it('can deprioritize unfinished work and resume it later', () => {
214234
function Bar(props) {
215235
Scheduler.unstable_yieldValue('Bar');
@@ -226,13 +246,13 @@ describe('ReactIncremental', () => {
226246
return (
227247
<div>
228248
<Bar>{props.text}</Bar>
229-
<section hidden={true}>
249+
<LegacyHiddenDiv hidden={true}>
230250
<Middle>{props.text}</Middle>
231-
</section>
251+
</LegacyHiddenDiv>
232252
<Bar>{props.text}</Bar>
233-
<footer hidden={true}>
253+
<LegacyHiddenDiv hidden={true}>
234254
<Middle>Footer</Middle>
235-
</footer>
255+
</LegacyHiddenDiv>
236256
</div>
237257
);
238258
}
@@ -255,6 +275,7 @@ describe('ReactIncremental', () => {
255275
expect(Scheduler).toFlushAndYield(['Middle', 'Middle']);
256276
});
257277

278+
// @gate enableLegacyHiddenType
258279
it('can deprioritize a tree from without dropping work', () => {
259280
function Bar(props) {
260281
Scheduler.unstable_yieldValue('Bar');
@@ -271,13 +292,13 @@ describe('ReactIncremental', () => {
271292
return (
272293
<div>
273294
<Bar>{props.text}</Bar>
274-
<section hidden={true}>
295+
<LegacyHiddenDiv hidden={true}>
275296
<Middle>{props.text}</Middle>
276-
</section>
297+
</LegacyHiddenDiv>
277298
<Bar>{props.text}</Bar>
278-
<footer hidden={true}>
299+
<LegacyHiddenDiv hidden={true}>
279300
<Middle>Footer</Middle>
280-
</footer>
301+
</LegacyHiddenDiv>
281302
</div>
282303
);
283304
}
@@ -1950,6 +1971,7 @@ describe('ReactIncremental', () => {
19501971
});
19511972
}
19521973

1974+
// @gate enableLegacyHiddenType
19531975
it('provides context when reusing work', () => {
19541976
class Intl extends React.Component {
19551977
static childContextTypes = {
@@ -1981,12 +2003,12 @@ describe('ReactIncremental', () => {
19812003
ReactNoop.render(
19822004
<Intl locale="fr">
19832005
<ShowLocale />
1984-
<div hidden="true">
2006+
<LegacyHiddenDiv hidden="true">
19852007
<ShowLocale />
19862008
<Intl locale="ru">
19872009
<ShowLocale />
19882010
</Intl>
1989-
</div>
2011+
</LegacyHiddenDiv>
19902012
<ShowLocale />
19912013
</Intl>,
19922014
);

packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ describe('ReactIncrementalErrorHandling', () => {
4545
);
4646
}
4747

48+
// TODO: Delete this once new API exists in both forks
49+
function LegacyHiddenDiv({hidden, children, ...props}) {
50+
if (gate(flags => flags.new)) {
51+
return (
52+
<div hidden={hidden} {...props}>
53+
<React.unstable_LegacyHidden mode={hidden ? 'hidden' : 'visible'}>
54+
{children}
55+
</React.unstable_LegacyHidden>
56+
</div>
57+
);
58+
} else {
59+
return (
60+
<div hidden={hidden} {...props}>
61+
{children}
62+
</div>
63+
);
64+
}
65+
}
66+
4867
it('recovers from errors asynchronously', () => {
4968
class ErrorBoundary extends React.Component {
5069
state = {error: null};
@@ -270,6 +289,7 @@ describe('ReactIncrementalErrorHandling', () => {
270289
expect(ReactNoop.getChildren()).toEqual([span('Everything is fine.')]);
271290
});
272291

292+
// @gate enableLegacyHiddenType
273293
it('does not include offscreen work when retrying after an error', () => {
274294
function App(props) {
275295
if (props.isBroken) {
@@ -280,9 +300,9 @@ describe('ReactIncrementalErrorHandling', () => {
280300
return (
281301
<>
282302
Everything is fine
283-
<div hidden={true}>
303+
<LegacyHiddenDiv hidden={true}>
284304
<div>Offscreen content</div>
285-
</div>
305+
</LegacyHiddenDiv>
286306
</>
287307
);
288308
}

0 commit comments

Comments
 (0)