Skip to content

Commit 2e0d0c1

Browse files
committed
Move useSyncExternalStore shim to a nested entrypoint
Also renames `useSyncExternalStoreExtra` to `useSyncExternalStoreWithSelector`. - 'use-sync-external-store/shim' -> A shim for `useSyncExternalStore` that works in React 16 and 17 (any release that supports hooks). The module will first check if the built-in React API exists, before falling back to the shim. - 'use-sync-external-store/with-selector' -> An extended version of `useSyncExternalStore` that also supports `selector` and `isEqual` options. It does _not_ shim `use-sync-external-store`; it composes the built-in React API. **Use this if you only support 18+.** - 'use-sync-external-store/shim/with-selector' -> Same API, but it composes `use-sync-external-store/shim` instead. **Use this for compataibility with 16 and 17.** - 'use-sync-external-store' -> Re-exports React's built-in API. Not meant to be used. It will warn and direct users to either the shim or the built-in API.
1 parent 9c8161b commit 2e0d0c1

29 files changed

+281
-77
lines changed

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

+17-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ let ReactDOMFizzServer;
1818
let Suspense;
1919
let SuspenseList;
2020
let useSyncExternalStore;
21-
let useSyncExternalStoreExtra;
21+
let useSyncExternalStoreWithSelector;
2222
let PropTypes;
2323
let textCache;
2424
let window;
@@ -43,11 +43,23 @@ describe('ReactDOMFizzServer', () => {
4343
Stream = require('stream');
4444
Suspense = React.Suspense;
4545
SuspenseList = React.SuspenseList;
46-
useSyncExternalStore = React.unstable_useSyncExternalStore;
47-
useSyncExternalStoreExtra = require('use-sync-external-store/extra')
48-
.useSyncExternalStoreExtra;
46+
4947
PropTypes = require('prop-types');
5048

49+
if (gate(flags => flags.source)) {
50+
// The `with-selector` module composes the main `use-sync-external-store`
51+
// entrypoint. In the compiled artifacts, this is resolved to the `shim`
52+
// implementation by our build config, but when running the tests against
53+
// the source files, we need to tell Jest how to resolve it. Because this
54+
// is a source module, this mock has no affect on the build tests.
55+
jest.mock('use-sync-external-store/src/useSyncExternalStore', () =>
56+
jest.requireActual('react'),
57+
);
58+
}
59+
useSyncExternalStore = React.unstable_useSyncExternalStore;
60+
useSyncExternalStoreWithSelector = require('use-sync-external-store/with-selector')
61+
.useSyncExternalStoreWithSelector;
62+
5163
textCache = new Map();
5264

5365
// Test Environment
@@ -1767,7 +1779,7 @@ describe('ReactDOMFizzServer', () => {
17671779
}
17681780

17691781
function App() {
1770-
const {env} = useSyncExternalStoreExtra(
1782+
const {env} = useSyncExternalStoreWithSelector(
17711783
subscribe,
17721784
getClientSnapshot,
17731785
getServerSnapshot,

packages/use-sync-external-store/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99

1010
'use strict';
1111

12-
export * from './src/useSyncExternalStore';
12+
export {useSyncExternalStore} from './src/useSyncExternalStore';

packages/use-sync-external-store/npm/extra.js

-7
This file was deleted.

packages/use-sync-external-store/npm/index.native.js

-7
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
if (process.env.NODE_ENV === 'production') {
4+
module.exports = require('../cjs/use-sync-external-store-shim.production.min.js');
5+
} else {
6+
module.exports = require('../cjs/use-sync-external-store-shim.development.js');
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
if (process.env.NODE_ENV === 'production') {
4+
module.exports = require('../cjs/use-sync-external-store-shim.native.production.min.js');
5+
} else {
6+
module.exports = require('../cjs/use-sync-external-store-shim.native.development.js');
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
if (process.env.NODE_ENV === 'production') {
4+
module.exports = require('../cjs/use-sync-external-store-shim/with-selector.production.min.js');
5+
} else {
6+
module.exports = require('../cjs/use-sync-external-store-shim/with-selector.development.js');
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
if (process.env.NODE_ENV === 'production') {
4+
module.exports = require('./cjs/use-sync-external-store-with-selector.production.min.js');
5+
} else {
6+
module.exports = require('./cjs/use-sync-external-store-with-selector.development.js');
7+
}

packages/use-sync-external-store/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
"README.md",
1313
"build-info.json",
1414
"index.js",
15-
"extra.js",
1615
"index.native.js",
16+
"with-selector.js",
17+
"with-selector.native.js",
18+
"shim/",
1719
"cjs/"
1820
],
1921
"license": "MIT",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
'use strict';
11+
12+
export {useSyncExternalStore} from 'use-sync-external-store/src/useSyncExternalStoreShim';

packages/use-sync-external-store/index.native.js renamed to packages/use-sync-external-store/shim/index.native.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99

1010
'use strict';
1111

12-
export * from './src/useSyncExternalStoreClient';
12+
export {useSyncExternalStore} from 'use-sync-external-store/src/useSyncExternalStoreShim';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
'use strict';
11+
12+
export {useSyncExternalStoreWithSelector} from 'use-sync-external-store/src/useSyncExternalStoreWithSelector';

packages/use-sync-external-store/src/__tests__/useSyncExternalStoreNative-test.js

+20-8
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ let React;
1515
let ReactNoop;
1616
let Scheduler;
1717
let useSyncExternalStore;
18-
let useSyncExternalStoreExtra;
18+
let useSyncExternalStoreWithSelector;
1919
let act;
2020

2121
// This tests the userspace shim of `useSyncExternalStore` in a server-rendering
@@ -43,18 +43,30 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
4343
return otherExports;
4444
});
4545

46-
jest.mock('use-sync-external-store', () =>
47-
jest.requireActual('use-sync-external-store/index.native'),
46+
jest.mock('use-sync-external-store/shim', () =>
47+
jest.requireActual('use-sync-external-store/shim/index.native'),
4848
);
4949

5050
React = require('react');
5151
ReactNoop = require('react-noop-renderer');
5252
Scheduler = require('scheduler');
5353
act = require('jest-react').act;
54-
useSyncExternalStore = require('use-sync-external-store')
54+
55+
if (gate(flags => flags.source)) {
56+
// The `shim/with-selector` module composes the main
57+
// `use-sync-external-store` entrypoint. In the compiled artifacts, this
58+
// is resolved to the `shim` implementation by our build config, but when
59+
// running the tests against the source files, we need to tell Jest how to
60+
// resolve it. Because this is a source module, this mock has no affect on
61+
// the build tests.
62+
jest.mock('use-sync-external-store/src/useSyncExternalStore', () =>
63+
jest.requireActual('use-sync-external-store/shim'),
64+
);
65+
}
66+
useSyncExternalStore = require('use-sync-external-store/shim')
5567
.useSyncExternalStore;
56-
useSyncExternalStoreExtra = require('use-sync-external-store/extra')
57-
.useSyncExternalStoreExtra;
68+
useSyncExternalStoreWithSelector = require('use-sync-external-store/shim/with-selector')
69+
.useSyncExternalStoreWithSelector;
5870
});
5971

6072
function Text({text}) {
@@ -130,7 +142,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
130142
const store = createExternalStore({a: 0, b: 0});
131143

132144
function A() {
133-
const {a} = useSyncExternalStoreExtra(
145+
const {a} = useSyncExternalStoreWithSelector(
134146
store.subscribe,
135147
store.getState,
136148
null,
@@ -140,7 +152,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
140152
return <Text text={'A' + a} />;
141153
}
142154
function B() {
143-
const {b} = useSyncExternalStoreExtra(
155+
const {b} = useSyncExternalStoreWithSelector(
144156
store.subscribe,
145157
store.getState,
146158
null,

packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js

+21-14
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
'use strict';
1111

1212
let useSyncExternalStore;
13-
let useSyncExternalStoreExtra;
13+
let useSyncExternalStoreWithSelector;
1414
let React;
1515
let ReactDOM;
1616
let Scheduler;
@@ -25,11 +25,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
2525
beforeEach(() => {
2626
jest.resetModules();
2727

28-
// Remove the built-in API from the React exports to force the package to
29-
// use the shim.
3028
if (!gate(flags => flags.supportsNativeUseSyncExternalStore)) {
31-
// and the non-variant tests for the shim.
32-
//
3329
// Remove useSyncExternalStore from the React imports so that we use the
3430
// shim instead. Also removing startTransition, since we use that to
3531
// detect outdated 18 alphas that don't yet include useSyncExternalStore.
@@ -64,10 +60,21 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
6460
// in both concurrent and legacy mode, I'm adding batching here.
6561
act = cb => internalAct(() => ReactDOM.unstable_batchedUpdates(cb));
6662

67-
useSyncExternalStore = require('use-sync-external-store')
63+
if (gate(flags => flags.source)) {
64+
// The `shim/with-selector` module composes the main
65+
// `use-sync-external-store` entrypoint. In the compiled artifacts, this
66+
// is resolved to the `shim` implementation by our build config, but when
67+
// running the tests against the source files, we need to tell Jest how to
68+
// resolve it. Because this is a source module, this mock has no affect on
69+
// the build tests.
70+
jest.mock('use-sync-external-store/src/useSyncExternalStore', () =>
71+
jest.requireActual('use-sync-external-store/shim'),
72+
);
73+
}
74+
useSyncExternalStore = require('use-sync-external-store/shim')
6875
.useSyncExternalStore;
69-
useSyncExternalStoreExtra = require('use-sync-external-store/extra')
70-
.useSyncExternalStoreExtra;
76+
useSyncExternalStoreWithSelector = require('use-sync-external-store/shim/with-selector')
77+
.useSyncExternalStoreWithSelector;
7178
});
7279

7380
function Text({text}) {
@@ -595,7 +602,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
595602

596603
function App() {
597604
Scheduler.unstable_yieldValue('App');
598-
const a = useSyncExternalStoreExtra(
605+
const a = useSyncExternalStoreWithSelector(
599606
store.subscribe,
600607
store.getState,
601608
null,
@@ -632,7 +639,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
632639
const store = createExternalStore({a: 0, b: 0});
633640

634641
function A() {
635-
const {a} = useSyncExternalStoreExtra(
642+
const {a} = useSyncExternalStoreWithSelector(
636643
store.subscribe,
637644
store.getState,
638645
null,
@@ -642,7 +649,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
642649
return <Text text={'A' + a} />;
643650
}
644651
function B() {
645-
const {b} = useSyncExternalStoreExtra(
652+
const {b} = useSyncExternalStoreWithSelector(
646653
store.subscribe,
647654
store.getState,
648655
null,
@@ -774,7 +781,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
774781
Scheduler.unstable_yieldValue('Inline selector');
775782
return [...state.items, 'C'];
776783
};
777-
const items = useSyncExternalStoreExtra(
784+
const items = useSyncExternalStoreWithSelector(
778785
store.subscribe,
779786
store.getState,
780787
null,
@@ -842,7 +849,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
842849
const selector = state => state.a.toUpperCase();
843850

844851
function App() {
845-
const a = useSyncExternalStoreExtra(
852+
const a = useSyncExternalStoreWithSelector(
846853
store.subscribe,
847854
store.getState,
848855
null,
@@ -877,7 +884,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
877884
const isEqual = (left, right) => left.a.trim() === right.a.trim();
878885

879886
function App() {
880-
const a = useSyncExternalStoreExtra(
887+
const a = useSyncExternalStoreWithSelector(
881888
store.subscribe,
882889
store.getState,
883890
null,

packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShimServer-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
4747
ReactDOMServer = require('react-dom/server');
4848
Scheduler = require('scheduler');
4949

50-
useSyncExternalStore = require('use-sync-external-store')
50+
useSyncExternalStore = require('use-sync-external-store/shim')
5151
.useSyncExternalStore;
5252
});
5353

packages/use-sync-external-store/extra.js renamed to packages/use-sync-external-store/src/forks/isServerEnvironment.native.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,4 @@
77
* @flow
88
*/
99

10-
'use strict';
11-
12-
export * from './src/useSyncExternalStoreExtra';
10+
export const isServerEnvironment = false;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
'use strict';
11+
12+
// Intentionally not using named imports because Rollup uses dynamic
13+
// dispatch for CommonJS interop named imports.
14+
import * as React from 'react';
15+
16+
export const useSyncExternalStore = React.unstable_useSyncExternalStore;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
'use strict';
11+
12+
// Intentionally not using named imports because Rollup uses dynamic
13+
// dispatch for CommonJS interop named imports.
14+
import * as shim from 'use-sync-external-store/shim';
15+
16+
export const useSyncExternalStore = shim.useSyncExternalStore;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import {canUseDOM} from 'shared/ExecutionEnvironment';
11+
12+
export const isServerEnvironment = !canUseDOM;

0 commit comments

Comments
 (0)