Remove concurrent apis from stable (#17088)

* Tests run in experimental mode by default

For local development, you usually want experiments enabled. Unless
the release channel is set with an environment variable, tests will
run with __EXPERIMENTAL__ set to `true`.

* Remove concurrent APIs from stable builds

Those who want to try concurrent mode should use the experimental
builds instead.

I've left the `unstable_` prefixed APIs in the Facebook build so we
can continue experimenting with them internally without blessing them
for widespread use.

* Turn on SSR flags in experimental build

* Remove prefixed concurrent APIs from www build

Instead we'll use the experimental builds when syncing to www.

* Remove "canary" from internal React version string
This commit is contained in:
Andrew Clark 2019-10-15 15:09:19 -07:00 committed by GitHub
parent 4cb399a433
commit 30c5daf943
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1978 additions and 2230 deletions

View File

@ -98,7 +98,10 @@ jobs:
- checkout
- *restore_yarn_cache
- *run_yarn
- run: yarn test --maxWorkers=2
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test --maxWorkers=2
test_source_experimental:
docker: *docker
@ -120,7 +123,10 @@ jobs:
- checkout
- *restore_yarn_cache
- *run_yarn
- run: yarn test-persistent --maxWorkers=2
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test-persistent --maxWorkers=2
test_source_prod:
docker: *docker
@ -130,7 +136,10 @@ jobs:
- checkout
- *restore_yarn_cache
- *run_yarn
- run: yarn test-prod --maxWorkers=2
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test-prod --maxWorkers=2
build:
docker: *docker
@ -217,7 +226,23 @@ jobs:
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run: yarn test-build --maxWorkers=2
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test-build --maxWorkers=2
test_build_experimental:
docker: *docker
environment: *environment
steps:
- checkout
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: experimental
command: yarn test-build --maxWorkers=2
test_build_devtools:
docker: *docker
@ -227,7 +252,10 @@ jobs:
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run: yarn test-build-devtools --maxWorkers=2
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test-build --maxWorkers=2
test_dom_fixtures:
docker: *docker
@ -238,6 +266,8 @@ jobs:
- *restore_yarn_cache
- run:
name: Run DOM fixture tests
environment:
RELEASE_CHANNEL: stable
command: |
cd fixtures/dom
yarn --frozen-lockfile
@ -265,7 +295,23 @@ jobs:
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run: yarn test-build-prod --maxWorkers=2
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test-build-prod --maxWorkers=2
test_build_prod_experimental:
docker: *docker
environment: *environment
steps:
- checkout
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: experimental
command: yarn test-build-prod --maxWorkers=2
workflows:
version: 2
@ -324,6 +370,12 @@ workflows:
- process_artifacts_experimental:
requires:
- build_experimental
- test_build_experimental:
requires:
- build_experimental
- test_build_prod_experimental:
requires:
- build_experimental
fuzz_tests:
triggers:

View File

@ -17,6 +17,7 @@ let TestRenderer;
let ARTTest;
global.__DEV__ = process.env.NODE_ENV !== 'production';
global.__EXPERIMENTAL__ = process.env.RELEASE_CHANNEL === 'experimental';
expect.extend(require('../toWarnDev'));
@ -176,8 +177,9 @@ it("doesn't warn if you use nested acts from different renderers", () => {
});
});
it('warns when using createRoot() + .render', () => {
const root = ReactDOM.unstable_createRoot(document.createElement('div'));
if (__EXPERIMENTAL__) {
it('warns when using createRoot() + .render', () => {
const root = ReactDOM.createRoot(document.createElement('div'));
expect(() => {
TestRenderer.act(() => {
root.render(<App />);
@ -191,4 +193,5 @@ it('warns when using createRoot() + .render', () => {
withoutStack: true,
}
);
});
});
}

View File

@ -1,493 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 1`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 2`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 3`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<B key="b">
<C key="c">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 4`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<C key="c">
<B key="b">
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 5`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<C key="c">
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 6`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<C key="c">
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 7`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<C key="c">
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 8`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<B key="b">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 9`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 10`] = `
[root]
▾ <Root>
<X>
<Suspense>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 11`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<B key="b">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense (Concurrent Mode) 12`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 1`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<A key="a">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 2`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<A key="a">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 3`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<A key="a">
<B key="b">
<C key="c">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 4`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<C key="c">
<B key="b">
<A key="a">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 5`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<C key="c">
<A key="a">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 6`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<C key="c">
<A key="a">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 7`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<C key="c">
<A key="a">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 8`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<A key="a">
<B key="b">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 9`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<A key="a">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 10`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 11`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<B key="b">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 12`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
▾ <MaybeSuspend>
<A key="a">
<Z>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 13`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 14`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 15`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<B key="b">
<C key="c">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 16`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<C key="c">
<B key="b">
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 17`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<C key="c">
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 18`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<C key="c">
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 19`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<C key="c">
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 20`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<B key="b">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 21`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 22`] = `
[root]
▾ <Root>
<X>
<Suspense>
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 23`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<B key="b">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test for Suspense without type change (Concurrent Mode) 24`] = `
[root]
▾ <Root>
<X>
▾ <Suspense>
<A key="a">
<Y>
`;
exports[`StoreStressConcurrent should handle a stress test with different tree operations (Concurrent Mode): 1: abcde 1`] = `
[root]
▾ <Parent>
<A key="a">
<B key="b">
<C key="c">
<D key="d">
<E key="e">
`;
exports[`StoreStressConcurrent should handle a stress test with different tree operations (Concurrent Mode): 2: abxde 1`] = `
[root]
▾ <Parent>
<A key="a">
<B key="b">
▾ <C key="c">
<X>
<D key="d">
<E key="e">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 1`] = `
[root]
▾ <Root>
<A key="a">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 2`] = `
[root]
▾ <Root>
<B key="b">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 3`] = `
[root]
▾ <Root>
<C key="c">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 4`] = `
[root]
▾ <Root>
<D key="d">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 5`] = `
[root]
▾ <Root>
<E key="e">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 6`] = `
[root]
▾ <Root>
<A key="a">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 7`] = `
[root]
▾ <Root>
<B key="b">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 8`] = `
[root]
▾ <Root>
<C key="c">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 9`] = `
[root]
▾ <Root>
<D key="d">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 10`] = `
[root]
▾ <Root>
<E key="e">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 11`] = `
[root]
▾ <Root>
<A key="a">
<B key="b">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 12`] = `
[root]
▾ <Root>
<B key="b">
<A key="a">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 13`] = `
[root]
▾ <Root>
<B key="b">
<C key="c">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 14`] = `
[root]
▾ <Root>
<C key="c">
<B key="b">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 15`] = `
[root]
▾ <Root>
<A key="a">
<C key="c">
`;
exports[`StoreStressConcurrent should handle stress test with reordering (Concurrent Mode) 16`] = `
[root]
▾ <Root>
<C key="c">
<A key="a">
`;

View File

@ -27,6 +27,11 @@ describe('StoreStressConcurrent', () => {
print = require('./storeSerializer').print;
});
if (!__EXPERIMENTAL__) {
it("empty test so Jest doesn't complain", () => {});
return;
}
// This is a stress test for the tree mount/update/unmount traversal.
// It renders different trees that should produce the same output.
it('should handle a stress test with different tree operations (Concurrent Mode)', () => {
@ -57,9 +62,19 @@ describe('StoreStressConcurrent', () => {
// 1. Render a normal version of [a, b, c, d, e].
let container = document.createElement('div');
// $FlowFixMe
let root = ReactDOM.unstable_createRoot(container);
let root = ReactDOM.createRoot(container);
act(() => root.render(<Parent>{[a, b, c, d, e]}</Parent>));
expect(store).toMatchSnapshot('1: abcde');
expect(store).toMatchInlineSnapshot(
`
[root]
<Parent>
<A key="a">
<B key="b">
<C key="c">
<D key="d">
<E key="e">
`,
);
expect(container.textContent).toMatch('abcde');
const snapshotForABCDE = print(store);
@ -68,7 +83,18 @@ describe('StoreStressConcurrent', () => {
act(() => {
setShowX(true);
});
expect(store).toMatchSnapshot('2: abxde');
expect(store).toMatchInlineSnapshot(
`
[root]
<Parent>
<A key="a">
<B key="b">
<C key="c">
<X>
<D key="d">
<E key="e">
`,
);
expect(container.textContent).toMatch('abxde');
const snapshotForABXDE = print(store);
@ -120,7 +146,7 @@ describe('StoreStressConcurrent', () => {
// Ensure fresh mount.
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
// Verify mounting 'abcde'.
act(() => root.render(<Parent>{cases[i]}</Parent>));
@ -150,7 +176,7 @@ describe('StoreStressConcurrent', () => {
// There'll be no unmounting until the very end.
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
for (let i = 0; i < cases.length; i++) {
// Verify mounting 'abcde'.
act(() => root.render(<Parent>{cases[i]}</Parent>));
@ -216,22 +242,80 @@ describe('StoreStressConcurrent', () => {
let snapshots = [];
let container = document.createElement('div');
// $FlowFixMe
let root = ReactDOM.unstable_createRoot(container);
let root = ReactDOM.createRoot(container);
for (let i = 0; i < steps.length; i++) {
act(() => root.render(<Root>{steps[i]}</Root>));
// We snapshot each step once so it doesn't regress.
expect(store).toMatchSnapshot();
snapshots.push(print(store));
act(() => root.unmount());
expect(print(store)).toBe('');
}
expect(snapshots).toMatchInlineSnapshot(`
Array [
"[root]
<Root>
<A key=\\"a\\">",
"[root]
<Root>
<B key=\\"b\\">",
"[root]
<Root>
<C key=\\"c\\">",
"[root]
<Root>
<D key=\\"d\\">",
"[root]
<Root>
<E key=\\"e\\">",
"[root]
<Root>
<A key=\\"a\\">",
"[root]
<Root>
<B key=\\"b\\">",
"[root]
<Root>
<C key=\\"c\\">",
"[root]
<Root>
<D key=\\"d\\">",
"[root]
<Root>
<E key=\\"e\\">",
"[root]
<Root>
<A key=\\"a\\">
<B key=\\"b\\">",
"[root]
<Root>
<B key=\\"b\\">
<A key=\\"a\\">",
"[root]
<Root>
<B key=\\"b\\">
<C key=\\"c\\">",
"[root]
<Root>
<C key=\\"c\\">
<B key=\\"b\\">",
"[root]
<Root>
<A key=\\"a\\">
<C key=\\"c\\">",
"[root]
<Root>
<C key=\\"c\\">
<A key=\\"a\\">",
]
`);
// 2. Verify that we can update from every step to every other step and back.
for (let i = 0; i < steps.length; i++) {
for (let j = 0; j < steps.length; j++) {
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() => root.render(<Root>{steps[i]}</Root>));
expect(print(store)).toMatch(snapshots[i]);
act(() => root.render(<Root>{steps[j]}</Root>));
@ -248,7 +332,7 @@ describe('StoreStressConcurrent', () => {
for (let j = 0; j < steps.length; j++) {
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>
@ -320,7 +404,7 @@ describe('StoreStressConcurrent', () => {
let snapshots = [];
let container = document.createElement('div');
// $FlowFixMe
let root = ReactDOM.unstable_createRoot(container);
let root = ReactDOM.createRoot(container);
for (let i = 0; i < steps.length; i++) {
act(() =>
root.render(
@ -331,13 +415,96 @@ describe('StoreStressConcurrent', () => {
</Root>,
),
);
// We snapshot each step once so it doesn't regress.
expect(store).toMatchSnapshot();
// We snapshot each step once so it doesn't regress.d
snapshots.push(print(store));
act(() => root.unmount());
expect(print(store)).toBe('');
}
expect(snapshots).toMatchInlineSnapshot(`
Array [
"[root]
<Root>
<X>
<Suspense>
<A key=\\"a\\">
<Y>",
"[root]
<Root>
<X>
<Suspense>
<A key=\\"a\\">
<Y>",
"[root]
<Root>
<X>
<Suspense>
<A key=\\"a\\">
<B key=\\"b\\">
<C key=\\"c\\">
<Y>",
"[root]
<Root>
<X>
<Suspense>
<C key=\\"c\\">
<B key=\\"b\\">
<A key=\\"a\\">
<Y>",
"[root]
<Root>
<X>
<Suspense>
<C key=\\"c\\">
<A key=\\"a\\">
<Y>",
"[root]
<Root>
<X>
<Suspense>
<C key=\\"c\\">
<A key=\\"a\\">
<Y>",
"[root]
<Root>
<X>
<Suspense>
<C key=\\"c\\">
<A key=\\"a\\">
<Y>",
"[root]
<Root>
<X>
<Suspense>
<A key=\\"a\\">
<B key=\\"b\\">
<Y>",
"[root]
<Root>
<X>
<Suspense>
<A key=\\"a\\">
<Y>",
"[root]
<Root>
<X>
<Suspense>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<B key=\\"b\\">
<Y>",
"[root]
<Root>
<X>
<Suspense>
<A key=\\"a\\">
<Y>",
]
`);
// 2. Verify check Suspense can render same steps as initial fallback content.
for (let i = 0; i < steps.length; i++) {
act(() =>
@ -364,7 +531,7 @@ describe('StoreStressConcurrent', () => {
// Always start with a fresh container and steps[i].
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>
@ -410,7 +577,7 @@ describe('StoreStressConcurrent', () => {
// Always start with a fresh container and steps[i].
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>
@ -468,7 +635,7 @@ describe('StoreStressConcurrent', () => {
// Always start with a fresh container and steps[i].
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>
@ -518,7 +685,7 @@ describe('StoreStressConcurrent', () => {
// Always start with a fresh container and steps[i].
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>
@ -572,7 +739,7 @@ describe('StoreStressConcurrent', () => {
// Always start with a fresh container and steps[i].
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>
@ -726,7 +893,7 @@ describe('StoreStressConcurrent', () => {
let snapshots = [];
let container = document.createElement('div');
// $FlowFixMe
let root = ReactDOM.unstable_createRoot(container);
let root = ReactDOM.createRoot(container);
for (let i = 0; i < steps.length; i++) {
act(() =>
root.render(
@ -740,7 +907,6 @@ describe('StoreStressConcurrent', () => {
),
);
// We snapshot each step once so it doesn't regress.
expect(store).toMatchSnapshot();
snapshots.push(print(store));
act(() => root.unmount());
expect(print(store)).toBe('');
@ -765,19 +931,126 @@ describe('StoreStressConcurrent', () => {
),
);
// We snapshot each step once so it doesn't regress.
expect(store).toMatchSnapshot();
fallbackSnapshots.push(print(store));
act(() => root.unmount());
expect(print(store)).toBe('');
}
expect(snapshots).toMatchInlineSnapshot(`
Array [
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<A key=\\"a\\">
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<A key=\\"a\\">
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<A key=\\"a\\">
<B key=\\"b\\">
<C key=\\"c\\">
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<C key=\\"c\\">
<B key=\\"b\\">
<A key=\\"a\\">
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<C key=\\"c\\">
<A key=\\"a\\">
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<C key=\\"c\\">
<A key=\\"a\\">
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<C key=\\"c\\">
<A key=\\"a\\">
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<A key=\\"a\\">
<B key=\\"b\\">
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<A key=\\"a\\">
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<B key=\\"b\\">
<Z>
<Y>",
"[root]
<Root>
<X>
<Suspense>
<MaybeSuspend>
<A key=\\"a\\">
<Z>
<Y>",
]
`);
// 3. Verify we can update from each step to each step in primary mode.
for (let i = 0; i < steps.length; i++) {
for (let j = 0; j < steps.length; j++) {
// Always start with a fresh container and steps[i].
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>
@ -829,7 +1102,7 @@ describe('StoreStressConcurrent', () => {
// Always start with a fresh container and steps[i].
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>
@ -896,7 +1169,7 @@ describe('StoreStressConcurrent', () => {
// Always start with a fresh container and steps[i].
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>
@ -948,7 +1221,7 @@ describe('StoreStressConcurrent', () => {
// Always start with a fresh container and steps[i].
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>
@ -1000,7 +1273,7 @@ describe('StoreStressConcurrent', () => {
// Always start with a fresh container and steps[i].
container = document.createElement('div');
// $FlowFixMe
root = ReactDOM.unstable_createRoot(container);
root = ReactDOM.createRoot(container);
act(() =>
root.render(
<Root>

View File

@ -47,6 +47,16 @@ describe('ReactDOMFiberAsync', () => {
expect(ops).toEqual(['Hi', 'Bye']);
});
if (__EXPERIMENTAL__) {
describe('concurrent mode', () => {
beforeEach(() => {
jest.resetModules();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
ReactDOM = require('react-dom');
Scheduler = require('scheduler');
});
it('does not perform deferred updates synchronously', () => {
let inputRef = React.createRef();
let asyncValueRef = React.createRef();
@ -83,14 +93,16 @@ describe('ReactDOMFiberAsync', () => {
);
}
}
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<Counter />);
Scheduler.unstable_flushAll();
expect(asyncValueRef.current.textContent).toBe('');
expect(syncValueRef.current.textContent).toBe('');
setUntrackedInputValue.call(inputRef.current, 'hello');
inputRef.current.dispatchEvent(new MouseEvent('input', {bubbles: true}));
inputRef.current.dispatchEvent(
new MouseEvent('input', {bubbles: true}),
);
// Should only flush non-deferred update.
expect(asyncValueRef.current.textContent).toBe('');
expect(syncValueRef.current.textContent).toBe('hello');
@ -102,17 +114,8 @@ describe('ReactDOMFiberAsync', () => {
expect(syncValueRef.current.textContent).toBe('hello');
});
describe('concurrent mode', () => {
beforeEach(() => {
jest.resetModules();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
ReactDOM = require('react-dom');
Scheduler = require('scheduler');
});
it('top-level updates are concurrent', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<div>Hi</div>);
expect(container.textContent).toEqual('');
Scheduler.unstable_flushAll();
@ -134,7 +137,7 @@ describe('ReactDOMFiberAsync', () => {
}
}
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<Component />);
expect(container.textContent).toEqual('');
Scheduler.unstable_flushAll();
@ -262,7 +265,7 @@ describe('ReactDOMFiberAsync', () => {
}
}
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<Component />);
Scheduler.unstable_flushAll();
@ -303,7 +306,7 @@ describe('ReactDOMFiberAsync', () => {
return this.state.counter;
}
}
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<Counter />);
Scheduler.unstable_flushAll();
expect(container.textContent).toEqual('0');
@ -414,7 +417,7 @@ describe('ReactDOMFiberAsync', () => {
}
}
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<Form />);
// Flush
Scheduler.unstable_flushAll();
@ -470,7 +473,9 @@ describe('ReactDOMFiberAsync', () => {
</button>
<button
onClick={
this.state.active ? this.submitForm : this.disabledSubmitForm
this.state.active
? this.submitForm
: this.disabledSubmitForm
}
ref={submitButtonRef}>
Submit
@ -480,7 +485,7 @@ describe('ReactDOMFiberAsync', () => {
}
}
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<Form />);
// Flush
Scheduler.unstable_flushAll();
@ -540,7 +545,7 @@ describe('ReactDOMFiberAsync', () => {
}
}
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<Form />);
// Flush
Scheduler.unstable_flushAll();
@ -572,7 +577,7 @@ describe('ReactDOMFiberAsync', () => {
describe('createSyncRoot', () => {
it('updates flush without yielding in the next event', () => {
const root = ReactDOM.unstable_createSyncRoot(container);
const root = ReactDOM.createSyncRoot(container);
function Text(props) {
Scheduler.unstable_yieldValue(props.text);
@ -594,10 +599,6 @@ describe('ReactDOMFiberAsync', () => {
expect(Scheduler).toFlushExpired(['A', 'B', 'C']);
expect(container.textContent).toEqual('ABC');
});
it('does not support createBatch', () => {
const root = ReactDOM.unstable_createSyncRoot(container);
expect(root.createBatch).toBe(undefined);
});
});
}
});

View File

@ -105,6 +105,7 @@ describe('ReactDOMHooks', () => {
expect(labelRef.current.innerHTML).toBe('abc');
});
if (__EXPERIMENTAL__) {
it('should not bail out when an update is scheduled from within an event handler in Concurrent Mode', () => {
const {createRef, useCallback, useState} = React;
@ -125,7 +126,7 @@ describe('ReactDOMHooks', () => {
const inputRef = createRef();
const labelRef = createRef();
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<Example inputRef={inputRef} labelRef={labelRef} />);
Scheduler.unstable_flushAll();
@ -139,4 +140,5 @@ describe('ReactDOMHooks', () => {
expect(labelRef.current.innerHTML).toBe('abc');
});
}
});

View File

@ -26,15 +26,22 @@ describe('ReactDOMRoot', () => {
Scheduler = require('scheduler');
});
if (!__EXPERIMENTAL__) {
it('createRoot is not exposed in stable build', () => {
expect(ReactDOM.createRoot).toBe(undefined);
});
return;
}
it('renders children', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<div>Hi</div>);
Scheduler.unstable_flushAll();
expect(container.textContent).toEqual('Hi');
});
it('unmounts children', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<div>Hi</div>);
Scheduler.unstable_flushAll();
expect(container.textContent).toEqual('Hi');
@ -57,7 +64,7 @@ describe('ReactDOMRoot', () => {
// Does not hydrate by default
const container1 = document.createElement('div');
container1.innerHTML = markup;
const root1 = ReactDOM.unstable_createRoot(container1);
const root1 = ReactDOM.createRoot(container1);
root1.render(
<div>
<span />
@ -68,7 +75,7 @@ describe('ReactDOMRoot', () => {
// Accepts `hydrate` option
const container2 = document.createElement('div');
container2.innerHTML = markup;
const root2 = ReactDOM.unstable_createRoot(container2, {hydrate: true});
const root2 = ReactDOM.createRoot(container2, {hydrate: true});
root2.render(
<div>
<span />
@ -81,7 +88,7 @@ describe('ReactDOMRoot', () => {
it('does not clear existing children', async () => {
container.innerHTML = '<div>a</div><div>b</div>';
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(
<div>
<span>c</span>
@ -102,12 +109,12 @@ describe('ReactDOMRoot', () => {
it('throws a good message on invalid containers', () => {
expect(() => {
ReactDOM.unstable_createRoot(<div>Hi</div>);
ReactDOM.createRoot(<div>Hi</div>);
}).toThrow('createRoot(...): Target container is not a DOM element.');
});
it('warns when rendering with legacy API into createRoot() container', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<div>Hi</div>);
Scheduler.unstable_flushAll();
expect(container.textContent).toEqual('Hi');
@ -130,7 +137,7 @@ describe('ReactDOMRoot', () => {
});
it('warns when hydrating with legacy API into createRoot() container', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<div>Hi</div>);
Scheduler.unstable_flushAll();
expect(container.textContent).toEqual('Hi');
@ -150,7 +157,7 @@ describe('ReactDOMRoot', () => {
});
it('warns when unmounting with legacy API (no previous content)', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<div>Hi</div>);
Scheduler.unstable_flushAll();
expect(container.textContent).toEqual('Hi');
@ -179,7 +186,7 @@ describe('ReactDOMRoot', () => {
// Currently createRoot().render() doesn't clear this.
container.appendChild(document.createElement('div'));
// The rest is the same as test above.
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<div>Hi</div>);
Scheduler.unstable_flushAll();
expect(container.textContent).toEqual('Hi');
@ -198,7 +205,7 @@ describe('ReactDOMRoot', () => {
it('warns when passing legacy container to createRoot()', () => {
ReactDOM.render(<div>Hi</div>, container);
expect(() => {
ReactDOM.unstable_createRoot(container);
ReactDOM.createRoot(container);
}).toWarnDev(
'You are calling ReactDOM.createRoot() on a container that was previously ' +
'passed to ReactDOM.render(). This is not supported.',

View File

@ -75,7 +75,6 @@ describe('ReactDOMServerPartialHydration', () => {
jest.resetModuleRegistry();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableSuspenseServerRenderer = true;
ReactFeatureFlags.enableSuspenseCallback = true;
ReactFeatureFlags.enableFlareAPI = true;
@ -90,6 +89,11 @@ describe('ReactDOMServerPartialHydration', () => {
useHover = require('react-interactions/events/hover').useHover;
});
if (!__EXPERIMENTAL__) {
it("empty test so Jest doesn't complain", () => {});
return;
}
it('hydrates a parent even if a child Suspense boundary is blocked', async () => {
let suspend = false;
let resolve;
@ -130,7 +134,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -200,7 +204,7 @@ describe('ReactDOMServerPartialHydration', () => {
// hydrating anyway.
suspend = true;
suspend2 = true;
let root = ReactDOM.unstable_createRoot(container, {
let root = ReactDOM.createRoot(container, {
hydrate: true,
hydrationOptions: {
onHydrated(node) {
@ -273,7 +277,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {
let root = ReactDOM.createRoot(container, {
hydrate: true,
hydrationOptions: {
onDeleted(node) {
@ -411,7 +415,7 @@ describe('ReactDOMServerPartialHydration', () => {
suspend = true;
act(() => {
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
});
@ -468,7 +472,7 @@ describe('ReactDOMServerPartialHydration', () => {
// hydrating anyway.
suspend = true;
act(() => {
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
});
@ -518,7 +522,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App text="Hello" className="hello" />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -587,7 +591,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App text="Hello" className="hello" />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -660,7 +664,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App text="Hello" className="hello" />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -732,7 +736,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App text="Hello" className="hello" />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -803,7 +807,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App text="Hello" className="hello" />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -889,7 +893,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(
<Context.Provider value={{text: 'Hello', className: 'hello'}}>
<App />
@ -971,7 +975,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(
<Context.Provider value={{text: 'Hello', className: 'hello'}}>
<App />
@ -1049,7 +1053,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we have the data available quickly for some reason.
suspend = false;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -1105,7 +1109,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we have the data available quickly for some reason.
suspend = false;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
// This will have exceeded the suspended time so we should timeout.
@ -1166,7 +1170,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we have the data available quickly for some reason.
suspend = false;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
// This will have exceeded the suspended time so we should timeout.
@ -1242,7 +1246,7 @@ describe('ReactDOMServerPartialHydration', () => {
// Attempt to hydrate the content.
suspend = false;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -1335,7 +1339,7 @@ describe('ReactDOMServerPartialHydration', () => {
// Attempt to hydrate the content.
suspend = false;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -1413,7 +1417,7 @@ describe('ReactDOMServerPartialHydration', () => {
let spanB = container.getElementsByTagName('span')[1];
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
suspend = true;
act(() => {
@ -1495,7 +1499,7 @@ describe('ReactDOMServerPartialHydration', () => {
let spanA = container.getElementsByTagName('span')[0];
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
suspend = true;
act(() => {
@ -1575,7 +1579,7 @@ describe('ReactDOMServerPartialHydration', () => {
// Put the suspense node in pending state.
suspenseNode.data = '$?';
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
suspend = true;
act(() => {
@ -1652,7 +1656,7 @@ describe('ReactDOMServerPartialHydration', () => {
let span = container.getElementsByTagName('span')[1];
suspend = false;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -1695,7 +1699,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -1748,7 +1752,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(
<ClassName.Provider value={'hello'}>
<App text="Hello" />
@ -1840,7 +1844,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -1914,7 +1918,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
// We'll do one click before hydrating.
@ -1995,7 +1999,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
// We'll do one click before hydrating.
@ -2072,7 +2076,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
// We'll do one click before hydrating.
@ -2151,7 +2155,7 @@ describe('ReactDOMServerPartialHydration', () => {
// On the client we don't have all data yet but we want to start
// hydrating anyway.
suspend = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
jest.runAllTimers();
@ -2216,7 +2220,7 @@ describe('ReactDOMServerPartialHydration', () => {
// We're going to use a different root as a parent.
// This lets us detect whether an event goes through React's event system.
let parentRoot = ReactDOM.unstable_createRoot(parentContainer);
let parentRoot = ReactDOM.createRoot(parentContainer);
parentRoot.render(<Parent />);
Scheduler.unstable_flushAll();
@ -2229,7 +2233,7 @@ describe('ReactDOMServerPartialHydration', () => {
suspend = true;
// Hydrate asynchronously.
let root = ReactDOM.unstable_createRoot(childContainer, {hydrate: true});
let root = ReactDOM.createRoot(childContainer, {hydrate: true});
root.render(<App />);
jest.runAllTimers();
Scheduler.unstable_flushAll();
@ -2319,7 +2323,7 @@ describe('ReactDOMServerPartialHydration', () => {
// hydrating anyway.
suspend1 = true;
suspend2 = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
@ -2434,7 +2438,7 @@ describe('ReactDOMServerPartialHydration', () => {
// hydrating anyway.
suspend1 = true;
suspend2 = true;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();

View File

@ -13,7 +13,6 @@ let React;
let ReactDOM;
let ReactDOMServer;
let Scheduler;
let ReactFeatureFlags;
let Suspense;
function dispatchMouseHoverEvent(to, from) {
@ -93,10 +92,6 @@ describe('ReactDOMServerSelectiveHydration', () => {
beforeEach(() => {
jest.resetModuleRegistry();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableSuspenseServerRenderer = true;
ReactFeatureFlags.enableSelectiveHydration = true;
React = require('react');
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
@ -104,6 +99,11 @@ describe('ReactDOMServerSelectiveHydration', () => {
Suspense = React.Suspense;
});
if (!__EXPERIMENTAL__) {
it("empty test so Jest doesn't complain", () => {});
return;
}
it('hydrates the target boundary synchronously during a click', async () => {
function Child({text}) {
Scheduler.unstable_yieldValue(text);
@ -144,7 +144,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
let span = container.getElementsByTagName('span')[1];
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
// Nothing has been hydrated so far.
@ -223,7 +223,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// A and D will be suspended. We'll click on D which should take
// priority, after we unsuspend.
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
// Nothing has been hydrated so far.
@ -309,7 +309,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// A and D will be suspended. We'll click on D which should take
// priority, after we unsuspend.
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
// Nothing has been hydrated so far.
@ -405,7 +405,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// A and D will be suspended. We'll click on D which should take
// priority, after we unsuspend.
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
// Nothing has been hydrated so far.
@ -474,7 +474,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
let spanB = container.getElementsByTagName('span')[1];
let spanC = container.getElementsByTagName('span')[2];
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
// Nothing has been hydrated so far.

View File

@ -14,16 +14,12 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegratio
let React;
let ReactDOM;
let ReactDOMServer;
let ReactFeatureFlags;
let ReactTestUtils;
function initModules() {
// Reset warning cache.
jest.resetModuleRegistry();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableSuspenseServerRenderer = true;
React = require('react');
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
@ -48,6 +44,11 @@ describe('ReactDOMServerSuspense', () => {
resetModules();
});
if (!__EXPERIMENTAL__) {
it("empty test so Jest doesn't complain", () => {});
return;
}
function Text(props) {
return <div>{props.text}</div>;
}
@ -125,7 +126,7 @@ describe('ReactDOMServerSuspense', () => {
expect(divB.textContent).toBe('B');
ReactTestUtils.act(() => {
const root = ReactDOM.unstable_createSyncRoot(parent, {hydrate: true});
const root = ReactDOM.createSyncRoot(parent, {hydrate: true});
root.render(example);
});

View File

@ -682,6 +682,7 @@ describe('ReactDOMServer', () => {
expect(markup).toBe('<div></div>');
});
if (!__EXPERIMENTAL__) {
it('throws for unsupported types on the server', () => {
expect(() => {
ReactDOMServer.renderToString(<React.Suspense />);
@ -714,6 +715,7 @@ describe('ReactDOMServer', () => {
ReactDOMServer.renderToString(<AsyncFoo />);
}).toThrow('ReactDOMServer does not yet support Suspense.');
});
}
it('does not get confused by throwing null', () => {
function Bad() {

View File

@ -500,11 +500,12 @@ describe('ReactDOMServerHydration', () => {
expect(element.textContent).toBe('Hello world');
});
if (__EXPERIMENTAL__) {
it('does not re-enter hydration after committing the first one', () => {
let finalHTML = ReactDOMServer.renderToString(<div />);
let container = document.createElement('div');
container.innerHTML = finalHTML;
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
let root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<div />);
Scheduler.unstable_flushAll();
root.render(null);
@ -514,152 +515,8 @@ describe('ReactDOMServerHydration', () => {
root.render(<div />);
Scheduler.unstable_flushAll();
});
it('does not invoke an event on a concurrent hydrating node until it commits', () => {
function Sibling({text}) {
Scheduler.unstable_yieldValue('Sibling');
return <span>Sibling</span>;
}
function Sibling2({text}) {
Scheduler.unstable_yieldValue('Sibling2');
return null;
}
let clicks = 0;
function Button() {
Scheduler.unstable_yieldValue('Button');
let [clicked, setClicked] = React.useState(false);
if (clicked) {
return null;
}
return (
<a
onClick={() => {
setClicked(true);
clicks++;
}}>
Click me
</a>
);
}
function App() {
return (
<div>
<Button />
<Sibling />
<Sibling2 />
</div>
);
}
let finalHTML = ReactDOMServer.renderToString(<App />);
let container = document.createElement('div');
container.innerHTML = finalHTML;
expect(Scheduler).toHaveYielded(['Button', 'Sibling', 'Sibling2']);
// We need this to be in the document since we'll dispatch events on it.
document.body.appendChild(container);
let a = container.getElementsByTagName('a')[0];
// Hydrate asynchronously.
let root = ReactDOM.unstable_createRoot(container, {hydrate: true});
root.render(<App />);
// We haven't started hydrating yet.
a.click();
// Clicking should not invoke the event yet because we haven't committed
// the hydration yet.
expect(clicks).toBe(0);
// Flush part way through the render.
if (__DEV__) {
// In DEV effects gets double invoked.
expect(Scheduler).toFlushAndYieldThrough(['Button', 'Button', 'Sibling']);
} else {
expect(Scheduler).toFlushAndYieldThrough(['Button', 'Sibling']);
}
expect(container.textContent).toBe('Click meSibling');
// We're now partially hydrated.
a.click();
// Clicking should not invoke the event yet because we haven't committed
// the hydration yet.
expect(clicks).toBe(0);
// Finish the rest of the hydration.
if (__DEV__) {
// In DEV effects gets double invoked.
expect(Scheduler).toFlushAndYield(['Sibling2', 'Button', 'Button']);
} else {
expect(Scheduler).toFlushAndYield(['Sibling2', 'Button']);
}
// We should have picked up both events now.
expect(clicks).toBe(2);
expect(container.textContent).toBe('Sibling');
document.body.removeChild(container);
});
it('does not invoke an event on a parent tree when a subtree is hydrating', () => {
let clicks = 0;
let childSlotRef = React.createRef();
function Parent() {
return <div onClick={() => clicks++} ref={childSlotRef} />;
}
function App() {
return (
<div>
<a>Click me</a>
</div>
);
}
let finalHTML = ReactDOMServer.renderToString(<App />);
let parentContainer = document.createElement('div');
let childContainer = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
document.body.appendChild(parentContainer);
// We're going to use a different root as a parent.
// This lets us detect whether an event goes through React's event system.
let parentRoot = ReactDOM.unstable_createRoot(parentContainer);
parentRoot.render(<Parent />);
Scheduler.unstable_flushAll();
childSlotRef.current.appendChild(childContainer);
childContainer.innerHTML = finalHTML;
let a = childContainer.getElementsByTagName('a')[0];
// Hydrate asynchronously.
let root = ReactDOM.unstable_createRoot(childContainer, {hydrate: true});
root.render(<App />);
// Nothing has rendered so far.
a.click();
expect(clicks).toBe(0);
Scheduler.unstable_flushAll();
// We're now full hydrated.
expect(clicks).toBe(1);
document.body.removeChild(parentContainer);
});
it('regression test: Suspense + hydration in legacy mode ', () => {
const element = document.createElement('div');
element.innerHTML = '<div>Hello World</div>';

View File

@ -27,22 +27,23 @@ function sleep(period) {
describe('ReactTestUtils.act()', () => {
// first we run all the tests with concurrent mode
if (__EXPERIMENTAL__) {
let concurrentRoot = null;
function renderConcurrent(el, dom) {
concurrentRoot = ReactDOM.unstable_createRoot(dom);
const renderConcurrent = (el, dom) => {
concurrentRoot = ReactDOM.createRoot(dom);
concurrentRoot.render(el);
}
};
function unmountConcurrent(_dom) {
const unmountConcurrent = _dom => {
if (concurrentRoot !== null) {
concurrentRoot.unmount();
concurrentRoot = null;
}
}
};
function rerenderConcurrent(el) {
const rerenderConcurrent = el => {
concurrentRoot.render(el);
}
};
runActTests(
'concurrent mode',
@ -50,6 +51,7 @@ describe('ReactTestUtils.act()', () => {
unmountConcurrent,
rerenderConcurrent,
);
}
// and then in sync mode
@ -71,24 +73,26 @@ describe('ReactTestUtils.act()', () => {
runActTests('legacy sync mode', renderSync, unmountSync, rerenderSync);
// and then in batched mode
if (__EXPERIMENTAL__) {
let batchedRoot = null;
function renderBatched(el, dom) {
batchedRoot = ReactDOM.unstable_createSyncRoot(dom);
const renderBatched = (el, dom) => {
batchedRoot = ReactDOM.createSyncRoot(dom);
batchedRoot.render(el);
}
};
function unmountBatched(dom) {
const unmountBatched = dom => {
if (batchedRoot !== null) {
batchedRoot.unmount();
batchedRoot = null;
}
}
};
function rerenderBatched(el) {
const rerenderBatched = el => {
batchedRoot.render(el);
}
};
runActTests('batched mode', renderBatched, unmountBatched, rerenderBatched);
}
describe('unacted effects', () => {
function App() {
@ -116,11 +120,10 @@ describe('ReactTestUtils.act()', () => {
]);
});
if (__EXPERIMENTAL__) {
it('warns in batched mode', () => {
expect(() => {
const root = ReactDOM.unstable_createSyncRoot(
document.createElement('div'),
);
const root = ReactDOM.createSyncRoot(document.createElement('div'));
root.render(<App />);
Scheduler.unstable_flushAll();
}).toWarnDev([
@ -131,9 +134,7 @@ describe('ReactTestUtils.act()', () => {
it('warns in concurrent mode', () => {
expect(() => {
const root = ReactDOM.unstable_createRoot(
document.createElement('div'),
);
const root = ReactDOM.createRoot(document.createElement('div'));
root.render(<App />);
Scheduler.unstable_flushAll();
}).toWarnDev([
@ -141,6 +142,7 @@ describe('ReactTestUtils.act()', () => {
'An update to App ran an effect, but was not wrapped in act(...)',
]);
});
}
});
});

View File

@ -27,9 +27,10 @@ it('does not warn when rendering in sync mode', () => {
}).toWarnDev([]);
});
it('should warn when rendering in concurrent mode', () => {
if (__EXPERIMENTAL__) {
it('should warn when rendering in concurrent mode', () => {
expect(() => {
ReactDOM.unstable_createRoot(document.createElement('div')).render(<App />);
ReactDOM.createRoot(document.createElement('div')).render(<App />);
}).toWarnDev(
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked ' +
'to guarantee consistent behaviour across tests and browsers.',
@ -37,15 +38,13 @@ it('should warn when rendering in concurrent mode', () => {
);
// does not warn twice
expect(() => {
ReactDOM.unstable_createRoot(document.createElement('div')).render(<App />);
ReactDOM.createRoot(document.createElement('div')).render(<App />);
}).toWarnDev([]);
});
});
it('should warn when rendering in batched mode', () => {
it('should warn when rendering in batched mode', () => {
expect(() => {
ReactDOM.unstable_createSyncRoot(document.createElement('div')).render(
<App />,
);
ReactDOM.createSyncRoot(document.createElement('div')).render(<App />);
}).toWarnDev(
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked ' +
'to guarantee consistent behaviour across tests and browsers.',
@ -53,8 +52,7 @@ it('should warn when rendering in batched mode', () => {
);
// does not warn twice
expect(() => {
ReactDOM.unstable_createSyncRoot(document.createElement('div')).render(
<App />,
);
ReactDOM.createSyncRoot(document.createElement('div')).render(<App />);
}).toWarnDev([]);
});
});
}

View File

@ -1292,6 +1292,7 @@ describe('ReactUpdates', () => {
expect(ops).toEqual(['Foo', 'Bar', 'Baz']);
});
if (__EXPERIMENTAL__) {
it('delays sync updates inside hidden subtrees in Concurrent Mode', () => {
const container = document.createElement('div');
@ -1323,7 +1324,7 @@ describe('ReactUpdates', () => {
);
}
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let hiddenDiv;
act(() => {
root.render(<Foo />);
@ -1335,7 +1336,11 @@ describe('ReactUpdates', () => {
'Foo#effect',
]);
} else {
expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Baz', 'Foo#effect']);
expect(Scheduler).toFlushAndYieldThrough([
'Foo',
'Baz',
'Foo#effect',
]);
}
hiddenDiv = container.firstChild.firstChild;
expect(hiddenDiv.hidden).toBe(true);
@ -1364,6 +1369,7 @@ describe('ReactUpdates', () => {
}
expect(hiddenDiv.innerHTML).toBe('<p>bar 1</p>');
});
}
it('can render ridiculously large number of roots without triggering infinite update loop error', () => {
class Foo extends React.Component {

View File

@ -61,7 +61,7 @@ import getComponentName from 'shared/getComponentName';
import invariant from 'shared/invariant';
import lowPriorityWarningWithoutStack from 'shared/lowPriorityWarningWithoutStack';
import warningWithoutStack from 'shared/warningWithoutStack';
import {enableStableConcurrentModeAPIs} from 'shared/ReactFeatureFlags';
import {exposeConcurrentModeAPIs} from 'shared/ReactFeatureFlags';
import {
getInstanceFromNode,
@ -593,27 +593,8 @@ const ReactDOM: Object = {
unstable_batchedUpdates: batchedUpdates,
// TODO remove this legacy method, unstable_discreteUpdates replaces it
unstable_interactiveUpdates: (fn, a, b, c) => {
flushDiscreteUpdates();
return discreteUpdates(fn, a, b, c);
},
unstable_discreteUpdates: discreteUpdates,
unstable_flushDiscreteUpdates: flushDiscreteUpdates,
flushSync: flushSync,
unstable_createRoot: createRoot,
unstable_createSyncRoot: createSyncRoot,
unstable_flushControlled: flushControlled,
unstable_scheduleHydration(target: Node) {
if (target) {
queueExplicitHydrationTarget(target);
}
},
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
// Keep in sync with ReactDOMUnstableNativeDependencies.js
// ReactTestUtils.js, and ReactTestUtilsAct.js. This is an array for better minification.
@ -678,9 +659,19 @@ function warnIfReactDOMContainerInDEV(container) {
}
}
if (enableStableConcurrentModeAPIs) {
if (exposeConcurrentModeAPIs) {
ReactDOM.createRoot = createRoot;
ReactDOM.createSyncRoot = createSyncRoot;
ReactDOM.unstable_discreteUpdates = discreteUpdates;
ReactDOM.unstable_flushDiscreteUpdates = flushDiscreteUpdates;
ReactDOM.unstable_flushControlled = flushControlled;
ReactDOM.unstable_scheduleHydration = target => {
if (target) {
queueExplicitHydrationTarget(target);
}
};
}
const foundDevTools = injectIntoDevTools({

View File

@ -474,6 +474,7 @@ describe('ChangeEventPlugin', () => {
}
});
if (__EXPERIMENTAL__) {
describe('concurrent mode', () => {
beforeEach(() => {
jest.resetModules();
@ -486,7 +487,7 @@ describe('ChangeEventPlugin', () => {
});
it('text input', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let input;
let ops = [];
@ -533,7 +534,7 @@ describe('ChangeEventPlugin', () => {
});
it('checkbox input', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let input;
let ops = [];
@ -595,7 +596,7 @@ describe('ChangeEventPlugin', () => {
});
it('textarea', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let textarea;
let ops = [];
@ -642,7 +643,7 @@ describe('ChangeEventPlugin', () => {
});
it('parent of input', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let input;
let ops = [];
@ -693,7 +694,7 @@ describe('ChangeEventPlugin', () => {
});
it('is async for non-input events', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let input;
let ops = [];
@ -758,7 +759,7 @@ describe('ChangeEventPlugin', () => {
const {act} = TestUtils;
const {useState} = React;
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
const target = React.createRef(null);
function Foo() {
@ -789,4 +790,5 @@ describe('ChangeEventPlugin', () => {
});
});
});
}
});

View File

@ -845,6 +845,7 @@ describe('DOMEventResponderSystem', () => {
buttonRef.current.dispatchEvent(createEvent('foobar'));
});
if (__EXPERIMENTAL__) {
it('should work with concurrent mode updates', async () => {
const log = [];
const TestResponder = createEventResponder({
@ -865,7 +866,7 @@ describe('DOMEventResponderSystem', () => {
);
}
let root = ReactDOM.unstable_createRoot(container);
let root = ReactDOM.createRoot(container);
root.render(<Test counter={0} />);
expect(Scheduler).toFlushAndYield(['Test']);
@ -893,6 +894,7 @@ describe('DOMEventResponderSystem', () => {
dispatchClickEvent(ref.current);
expect(log).toEqual([{counter: 1}]);
});
}
it('should correctly pass through event properties', () => {
const timeStamps = [];

View File

@ -230,6 +230,7 @@ describe('SimpleEventPlugin', function() {
expect(button.textContent).toEqual('Count: 3');
});
if (__EXPERIMENTAL__) {
describe('interactive events, in concurrent mode', () => {
beforeEach(() => {
jest.resetModules();
@ -241,7 +242,7 @@ describe('SimpleEventPlugin', function() {
it('flushes pending interactive work before extracting event handler', () => {
container = document.createElement('div');
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
document.body.appendChild(container);
let ops = [];
@ -321,7 +322,7 @@ describe('SimpleEventPlugin', function() {
it('end result of many interactive updates is deterministic', () => {
container = document.createElement('div');
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
document.body.appendChild(container);
let button;
@ -421,12 +422,14 @@ describe('SimpleEventPlugin', function() {
}
// Initial mount
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<Wrapper />);
expect(Scheduler).toFlushAndYield([
'High-pri count: 0, Low-pri count: 0',
]);
expect(button.textContent).toEqual('High-pri count: 0, Low-pri count: 0');
expect(button.textContent).toEqual(
'High-pri count: 0, Low-pri count: 0',
);
function click() {
button.dispatchEvent(
@ -441,8 +444,12 @@ describe('SimpleEventPlugin', function() {
// Click again. This will force the previous discrete update to flush. But
// only the high-pri count will increase.
click();
expect(Scheduler).toHaveYielded(['High-pri count: 1, Low-pri count: 0']);
expect(button.textContent).toEqual('High-pri count: 1, Low-pri count: 0');
expect(Scheduler).toHaveYielded([
'High-pri count: 1, Low-pri count: 0',
]);
expect(button.textContent).toEqual(
'High-pri count: 1, Low-pri count: 0',
);
// Click the button many more times
click();
@ -467,9 +474,12 @@ describe('SimpleEventPlugin', function() {
'High-pri count: 8, Low-pri count: 0',
'High-pri count: 8, Low-pri count: 8',
]);
expect(button.textContent).toEqual('High-pri count: 8, Low-pri count: 8');
expect(button.textContent).toEqual(
'High-pri count: 8, Low-pri count: 8',
);
});
});
}
describe('iOS bubbling click fix', function() {
// See http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html

View File

@ -750,9 +750,10 @@ describe('Input event responder', () => {
}
});
if (__EXPERIMENTAL__) {
describe('concurrent mode', () => {
it('text input', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let input;
let ops = [];
@ -777,7 +778,9 @@ describe('Input event responder', () => {
render() {
ops.push(`render: ${this.state.value}`);
const controlledValue =
this.state.value === 'changed' ? 'changed [!]' : this.state.value;
this.state.value === 'changed'
? 'changed [!]'
: this.state.value;
return (
<Component
onChange={this.onChange}
@ -812,7 +815,7 @@ describe('Input event responder', () => {
});
it('checkbox input', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let input;
let ops = [];
@ -887,7 +890,7 @@ describe('Input event responder', () => {
});
it('textarea', () => {
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let textarea;
let ops = [];
@ -912,7 +915,9 @@ describe('Input event responder', () => {
render() {
ops.push(`render: ${this.state.value}`);
const controlledValue =
this.state.value === 'changed' ? 'changed [!]' : this.state.value;
this.state.value === 'changed'
? 'changed [!]'
: this.state.value;
return (
<Component
onChange={this.onChange}
@ -946,6 +951,7 @@ describe('Input event responder', () => {
expect(textarea.value).toBe('changed [!]');
});
});
}
});
it('expect displayName to show up for event component', () => {

View File

@ -34,6 +34,11 @@ describe('mixing responders with the heritage event system', () => {
container = null;
});
if (!__EXPERIMENTAL__) {
it("empty test so Jest doesn't complain", () => {});
return;
}
it('should properly only flush sync once when the event systems are mixed', () => {
const useTap = require('react-interactions/events/tap').useTap;
const ref = React.createRef();
@ -66,7 +71,7 @@ describe('mixing responders with the heritage event system', () => {
}
const newContainer = document.createElement('div');
const root = ReactDOM.unstable_createRoot(newContainer);
const root = ReactDOM.createRoot(newContainer);
document.body.appendChild(newContainer);
root.render(<MyComponent />);
Scheduler.unstable_flushAll();
@ -137,7 +142,7 @@ describe('mixing responders with the heritage event system', () => {
}
const newContainer = document.createElement('div');
const root = ReactDOM.unstable_createRoot(newContainer);
const root = ReactDOM.createRoot(newContainer);
document.body.appendChild(newContainer);
root.render(<MyComponent />);
Scheduler.unstable_flushAll();
@ -216,7 +221,7 @@ describe('mixing responders with the heritage event system', () => {
const newContainer = document.createElement('div');
document.body.appendChild(newContainer);
const root = ReactDOM.unstable_createRoot(newContainer);
const root = ReactDOM.createRoot(newContainer);
root.render(<MyComponent />);
Scheduler.unstable_flushAll();
@ -238,7 +243,7 @@ describe('mixing responders with the heritage event system', () => {
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
const useTap = require('react-interactions/events/tap').useTap;
const useInput = require('react-interactions/events/input').useInput;
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let input;
let ops = [];

View File

@ -2394,7 +2394,7 @@ describe('ReactFresh', () => {
});
it('can hot reload offscreen components', () => {
if (__DEV__) {
if (__DEV__ && __EXPERIMENTAL__) {
const AppV1 = prepare(() => {
function Hello() {
React.useLayoutEffect(() => {
@ -2421,7 +2421,7 @@ describe('ReactFresh', () => {
};
});
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
root.render(<AppV1 offscreen={true} />);
expect(Scheduler).toFlushAndYieldThrough(['App#layout']);
const el = container.firstChild;

View File

@ -27,7 +27,6 @@ function loadModules() {
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.debugRenderPhaseSideEffects = false;
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
ReactFeatureFlags.enableSuspenseServerRenderer = true;
ReactFeatureFlags.enableProfilerTimer = true;
ReactFeatureFlags.enableSchedulerTracing = true;
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
@ -64,6 +63,11 @@ describe('ReactDOMTracing', () => {
loadModules();
});
if (!__EXPERIMENTAL__) {
it("empty test so Jest doesn't complain", () => {});
return;
}
describe('interaction tracing', () => {
describe('hidden', () => {
it('traces interaction through hidden subtree', () => {
@ -101,7 +105,7 @@ describe('ReactDOMTracing', () => {
const onRender = jest.fn();
const container = document.createElement('div');
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
SchedulerTracing.unstable_trace('initialization', 0, () => {
interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
TestUtils.act(() => {
@ -171,7 +175,7 @@ describe('ReactDOMTracing', () => {
const onRender = jest.fn();
const container = document.createElement('div');
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
SchedulerTracing.unstable_trace('initialization', 0, () => {
interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
@ -250,7 +254,7 @@ describe('ReactDOMTracing', () => {
const onRender = jest.fn();
const container = document.createElement('div');
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
SchedulerTracing.unstable_trace('initialization', 0, () => {
interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
TestUtils.act(() => {
@ -344,7 +348,7 @@ describe('ReactDOMTracing', () => {
const onRender = jest.fn();
const container = document.createElement('div');
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
// Schedule some idle work without any interactions.
TestUtils.act(() => {
@ -448,7 +452,7 @@ describe('ReactDOMTracing', () => {
const onRender = jest.fn();
const container = document.createElement('div');
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
TestUtils.act(() => {
root.render(
@ -545,7 +549,7 @@ describe('ReactDOMTracing', () => {
}
const container = document.createElement('div');
const root = ReactDOM.unstable_createRoot(container);
const root = ReactDOM.createRoot(container);
let interaction;
@ -627,7 +631,7 @@ describe('ReactDOMTracing', () => {
let interaction;
const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
const root = ReactDOM.createRoot(container, {hydrate: true});
// Hydrate it.
SchedulerTracing.unstable_trace('initialization', 0, () => {
@ -686,7 +690,7 @@ describe('ReactDOMTracing', () => {
let interaction;
const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
const root = ReactDOM.createRoot(container, {hydrate: true});
// Start hydrating but simulate blocking for suspense data.
suspend = true;
@ -755,7 +759,7 @@ describe('ReactDOMTracing', () => {
let interaction;
const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
const root = ReactDOM.createRoot(container, {hydrate: true});
// Hydrate without suspending to fill in the client-rendered content.
suspend = false;

View File

@ -54,6 +54,11 @@ describe('ProfilerDOM', () => {
return props.text;
}
if (!__EXPERIMENTAL__) {
it("empty test so Jest doesn't complain", () => {});
return;
}
it('should correctly trace interactions for async roots', async () => {
let resolve;
let thenable = {
@ -75,7 +80,7 @@ describe('ProfilerDOM', () => {
}
const element = document.createElement('div');
const root = ReactDOM.unstable_createRoot(element);
const root = ReactDOM.createRoot(element);
let interaction;
let wrappedResolve;

View File

@ -31,9 +31,9 @@ export const enableProfilerTimer = __PROFILE__;
// Trace which interactions trigger each commit.
export const enableSchedulerTracing = __PROFILE__;
// Only used in www builds.
export const enableSuspenseServerRenderer = false; // TODO: __DEV__? Here it might just be false.
export const enableSelectiveHydration = false;
// SSR experiments
export const enableSuspenseServerRenderer = __EXPERIMENTAL__;
export const enableSelectiveHydration = __EXPERIMENTAL__;
// Only used in www builds.
export const enableSchedulerDebugging = false;
@ -52,7 +52,7 @@ export const disableInputAttributeSyncing = false;
// These APIs will no longer be "unstable" in the upcoming 16.7 release,
// Control this behavior with a flag to support 16.6 minor releases in the meanwhile.
export const enableStableConcurrentModeAPIs = __EXPERIMENTAL__;
export const exposeConcurrentModeAPIs = __EXPERIMENTAL__;
export const warnAboutShorthandPropertyCollision = false;

View File

@ -23,7 +23,7 @@ export const enableProfilerTimer = __PROFILE__;
export const enableSchedulerTracing = __PROFILE__;
export const enableSuspenseServerRenderer = false;
export const enableSelectiveHydration = false;
export const enableStableConcurrentModeAPIs = false;
export const exposeConcurrentModeAPIs = false;
export const warnAboutShorthandPropertyCollision = false;
export const enableSchedulerDebugging = false;
export const debugRenderPhaseSideEffectsForStrictMode = true;

View File

@ -23,7 +23,7 @@ export const enableSuspenseServerRenderer = false;
export const enableSelectiveHydration = false;
export const disableJavaScriptURLs = false;
export const disableInputAttributeSyncing = false;
export const enableStableConcurrentModeAPIs = false;
export const exposeConcurrentModeAPIs = false;
export const warnAboutShorthandPropertyCollision = false;
export const enableSchedulerDebugging = false;
export const enableFlareAPI = false;

View File

@ -23,7 +23,7 @@ export const enableSuspenseServerRenderer = false;
export const enableSelectiveHydration = false;
export const disableJavaScriptURLs = false;
export const disableInputAttributeSyncing = false;
export const enableStableConcurrentModeAPIs = false;
export const exposeConcurrentModeAPIs = false;
export const warnAboutShorthandPropertyCollision = false;
export const enableSchedulerDebugging = false;
export const enableFlareAPI = false;

View File

@ -23,7 +23,7 @@ export const enableSuspenseServerRenderer = false;
export const enableSelectiveHydration = false;
export const disableJavaScriptURLs = false;
export const disableInputAttributeSyncing = false;
export const enableStableConcurrentModeAPIs = false;
export const exposeConcurrentModeAPIs = false;
export const warnAboutShorthandPropertyCollision = false;
export const enableSchedulerDebugging = false;
export const enableFlareAPI = false;

View File

@ -21,7 +21,7 @@ export const enableProfilerTimer = __PROFILE__;
export const enableSchedulerTracing = __PROFILE__;
export const enableSuspenseServerRenderer = false;
export const enableSelectiveHydration = false;
export const enableStableConcurrentModeAPIs = false;
export const exposeConcurrentModeAPIs = false;
export const enableSchedulerDebugging = false;
export const disableJavaScriptURLs = false;
export const enableFlareAPI = true;

View File

@ -39,7 +39,7 @@ export const warnAboutStringRefs = false;
export const warnAboutDefaultPropsOnFunctionComponents = false;
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
export const enableStableConcurrentModeAPIs = false;
export const exposeConcurrentModeAPIs = false;
export const enableSuspenseServerRenderer = true;

View File

@ -7,7 +7,15 @@ if (NODE_ENV !== 'development' && NODE_ENV !== 'production') {
global.__DEV__ = NODE_ENV === 'development';
global.__PROFILE__ = NODE_ENV === 'development';
global.__UMD__ = false;
global.__EXPERIMENTAL__ = process.env.RELEASE_CHANNEL === 'experimental';
const RELEASE_CHANNEL = process.env.RELEASE_CHANNEL;
// Default to running tests in experimental mode. If the release channel is
// set via an environment variable, then check if it's "experimental".
global.__EXPERIMENTAL__ =
typeof RELEASE_CHANNEL === 'string'
? RELEASE_CHANNEL === 'experimental'
: true;
if (typeof window !== 'undefined') {
global.requestIdleCallback = function(callback) {

View File

@ -103,8 +103,8 @@ const getBuildInfo = async () => {
join(cwd, 'packages', 'react', 'package.json')
);
const reactVersion = isExperimental
? `${packageJSON.version}-experimental-canary-${commit}`
: `${packageJSON.version}-canary-${commit}`;
? `${packageJSON.version}-experimental-${commit}`
: `${packageJSON.version}-${commit}`;
return {branch, buildNumber, checksum, commit, reactVersion, version};
};