Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ReactVersions.js
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ const stablePackages = {
'react-refresh': '0.11.0',
'react-test-renderer': ReactVersion,
'use-subscription': '1.6.0',
'use-sync-external-store': '1.0.0',
scheduler: '0.21.0',
};

@@ -47,7 +48,6 @@ const experimentalPackages = [
'react-fs',
'react-pg',
'react-server-dom-webpack',
'use-sync-external-store',
];

module.exports = {
Original file line number Diff line number Diff line change
@@ -1055,9 +1055,8 @@ describe('ReactHooksInspectionIntegration', () => {
]);
});

// @gate experimental || www
it('should support composite useSyncExternalStore hook', () => {
const useSyncExternalStore = React.unstable_useSyncExternalStore;
const useSyncExternalStore = React.useSyncExternalStore;
function Foo() {
const value = useSyncExternalStore(
() => () => {},
26 changes: 17 additions & 9 deletions packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ let ReactDOMFizzServer;
let Suspense;
let SuspenseList;
let useSyncExternalStore;
let useSyncExternalStoreExtra;
let useSyncExternalStoreWithSelector;
let PropTypes;
let textCache;
let window;
@@ -43,11 +43,23 @@ describe('ReactDOMFizzServer', () => {
Stream = require('stream');
Suspense = React.Suspense;
SuspenseList = React.SuspenseList;
useSyncExternalStore = React.unstable_useSyncExternalStore;
useSyncExternalStoreExtra = require('use-sync-external-store/extra')
.useSyncExternalStoreExtra;

PropTypes = require('prop-types');

if (gate(flags => flags.source)) {
// The `with-selector` module composes the main `use-sync-external-store`
// entrypoint. In the compiled artifacts, this is resolved to the `shim`
// implementation by our build config, but when running the tests against
// the source files, we need to tell Jest how to resolve it. Because this
// is a source module, this mock has no affect on the build tests.
jest.mock('use-sync-external-store/src/useSyncExternalStore', () =>
jest.requireActual('react'),
);
}
useSyncExternalStore = React.useSyncExternalStore;
useSyncExternalStoreWithSelector = require('use-sync-external-store/with-selector')
.useSyncExternalStoreWithSelector;

textCache = new Map();

// Test Environment
@@ -1663,7 +1675,6 @@ describe('ReactDOMFizzServer', () => {
);
});

// @gate supportsNativeUseSyncExternalStore
// @gate experimental
it('calls getServerSnapshot instead of getSnapshot', async () => {
const ref = React.createRef();
@@ -1734,7 +1745,6 @@ describe('ReactDOMFizzServer', () => {

// The selector implementation uses the lazy ref initialization pattern
// @gate !(enableUseRefAccessWarning && __DEV__)
// @gate supportsNativeUseSyncExternalStore
// @gate experimental
it('calls getServerSnapshot instead of getSnapshot (with selector and isEqual)', async () => {
// Same as previous test, but with a selector that returns a complex object
@@ -1767,7 +1777,7 @@ describe('ReactDOMFizzServer', () => {
}

function App() {
const {env} = useSyncExternalStoreExtra(
const {env} = useSyncExternalStoreWithSelector(
subscribe,
getClientSnapshot,
getServerSnapshot,
@@ -1815,7 +1825,6 @@ describe('ReactDOMFizzServer', () => {
expect(ref.current).toEqual(serverRenderedDiv);
});

// @gate supportsNativeUseSyncExternalStore
// @gate experimental
it(
'errors during hydration force a client render at the nearest Suspense ' +
@@ -1964,7 +1973,6 @@ describe('ReactDOMFizzServer', () => {
},
);

// @gate supportsNativeUseSyncExternalStore
// @gate experimental
it(
'errors during hydration force a client render at the nearest Suspense ' +
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ describe('useSyncExternalStore', () => {
useImperativeHandle = React.useImperativeHandle;
forwardRef = React.forwardRef;
useRef = React.useRef;
useSyncExternalStore = React.unstable_useSyncExternalStore;
useSyncExternalStore = React.useSyncExternalStore;
startTransition = React.startTransition;

act = require('jest-react').act;
@@ -70,7 +70,6 @@ describe('useSyncExternalStore', () => {
};
}

// @gate supportsNativeUseSyncExternalStore
test(
'detects interleaved mutations during a concurrent read before ' +
'layout effects fire',
1 change: 0 additions & 1 deletion packages/react/index.classic.fb.js
Original file line number Diff line number Diff line change
@@ -54,7 +54,6 @@ export {
useMutableSource,
useMutableSource as unstable_useMutableSource,
useSyncExternalStore,
useSyncExternalStore as unstable_useSyncExternalStore,
useReducer,
useRef,
useState,
2 changes: 1 addition & 1 deletion packages/react/index.experimental.js
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ export {
useLayoutEffect,
useMemo,
useMutableSource as unstable_useMutableSource,
useSyncExternalStore as unstable_useSyncExternalStore,
useSyncExternalStore,
useReducer,
useRef,
useState,
1 change: 0 additions & 1 deletion packages/react/index.js
Original file line number Diff line number Diff line change
@@ -73,7 +73,6 @@ export {
useMemo,
useMutableSource,
useSyncExternalStore,
useSyncExternalStore as unstable_useSyncExternalStore,
useReducer,
useRef,
useState,
1 change: 0 additions & 1 deletion packages/react/index.modern.fb.js
Original file line number Diff line number Diff line change
@@ -53,7 +53,6 @@ export {
useMutableSource,
useMutableSource as unstable_useMutableSource,
useSyncExternalStore,
useSyncExternalStore as unstable_useSyncExternalStore,
useReducer,
useRef,
useState,
1 change: 1 addition & 0 deletions packages/react/index.stable.js
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ export {
useLayoutEffect,
useMemo,
useMutableSource as unstable_useMutableSource,
useSyncExternalStore,
useReducer,
useRef,
useState,
2 changes: 1 addition & 1 deletion packages/use-sync-external-store/index.js
Original file line number Diff line number Diff line change
@@ -9,4 +9,4 @@

'use strict';

export * from './src/useSyncExternalStore';
export {useSyncExternalStore} from './src/useSyncExternalStore';
7 changes: 0 additions & 7 deletions packages/use-sync-external-store/npm/extra.js

This file was deleted.

7 changes: 0 additions & 7 deletions packages/use-sync-external-store/npm/index.native.js

This file was deleted.

7 changes: 7 additions & 0 deletions packages/use-sync-external-store/npm/shim/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

if (process.env.NODE_ENV === 'production') {
module.exports = require('../cjs/use-sync-external-store-shim.production.min.js');
} else {
module.exports = require('../cjs/use-sync-external-store-shim.development.js');
}
7 changes: 7 additions & 0 deletions packages/use-sync-external-store/npm/shim/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

if (process.env.NODE_ENV === 'production') {
module.exports = require('../cjs/use-sync-external-store-shim.native.production.min.js');
} else {
module.exports = require('../cjs/use-sync-external-store-shim.native.development.js');
}
7 changes: 7 additions & 0 deletions packages/use-sync-external-store/npm/shim/with-selector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

if (process.env.NODE_ENV === 'production') {
module.exports = require('../cjs/use-sync-external-store-shim/with-selector.production.min.js');
} else {
module.exports = require('../cjs/use-sync-external-store-shim/with-selector.development.js');
}
7 changes: 7 additions & 0 deletions packages/use-sync-external-store/npm/with-selector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/use-sync-external-store-with-selector.production.min.js');
} else {
module.exports = require('./cjs/use-sync-external-store-with-selector.development.js');
}
4 changes: 3 additions & 1 deletion packages/use-sync-external-store/package.json
Original file line number Diff line number Diff line change
@@ -12,8 +12,10 @@
"README.md",
"build-info.json",
"index.js",
"extra.js",
"index.native.js",
"with-selector.js",
"with-selector.native.js",
"shim/",
"cjs/"
],
"license": "MIT",
12 changes: 12 additions & 0 deletions packages/use-sync-external-store/shim/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

'use strict';

export {useSyncExternalStore} from 'use-sync-external-store/src/useSyncExternalStoreShim';
Original file line number Diff line number Diff line change
@@ -9,4 +9,4 @@

'use strict';

export * from './src/useSyncExternalStoreClient';
export {useSyncExternalStore} from 'use-sync-external-store/src/useSyncExternalStoreShim';
12 changes: 12 additions & 0 deletions packages/use-sync-external-store/shim/with-selector/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

'use strict';

export {useSyncExternalStoreWithSelector} from 'use-sync-external-store/src/useSyncExternalStoreWithSelector';
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ let React;
let ReactNoop;
let Scheduler;
let useSyncExternalStore;
let useSyncExternalStoreExtra;
let useSyncExternalStoreWithSelector;
let act;

// This tests the userspace shim of `useSyncExternalStore` in a server-rendering
@@ -36,25 +36,40 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
startTransition: _,
// eslint-disable-next-line no-unused-vars
useSyncExternalStore: __,
// eslint-disable-next-line no-unused-vars
unstable_useSyncExternalStore: ___,
...otherExports
} = jest.requireActual('react');
return otherExports;
});

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

React = require('react');
ReactNoop = require('react-noop-renderer');
Scheduler = require('scheduler');
act = require('jest-react').act;
useSyncExternalStore = require('use-sync-external-store')

if (gate(flags => flags.source)) {
// The `shim/with-selector` module composes the main
// `use-sync-external-store` entrypoint. In the compiled artifacts, this
// is resolved to the `shim` implementation by our build config, but when
// running the tests against the source files, we need to tell Jest how to
// resolve it. Because this is a source module, this mock has no affect on
// the build tests.
jest.mock('use-sync-external-store/src/useSyncExternalStore', () =>
jest.requireActual('use-sync-external-store/shim'),
);
jest.mock('use-sync-external-store/src/isServerEnvironment', () =>
jest.requireActual(
'use-sync-external-store/src/forks/isServerEnvironment.native',
),
);
}
useSyncExternalStore = require('use-sync-external-store/shim')
.useSyncExternalStore;
useSyncExternalStoreExtra = require('use-sync-external-store/extra')
.useSyncExternalStoreExtra;
useSyncExternalStoreWithSelector = require('use-sync-external-store/shim/with-selector')
.useSyncExternalStoreWithSelector;
});

function Text({text}) {
@@ -105,32 +120,12 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
expect(root).toMatchRenderedOutput('client');
});

test('native version', async () => {
const store = createExternalStore('client');

function App() {
const text = useSyncExternalStore(
store.subscribe,
store.getState,
() => 'server',
);
return <Text text={text} />;
}

const root = ReactNoop.createRoot();
await act(() => {
root.render(<App />);
});
expect(Scheduler).toHaveYielded(['client']);
expect(root).toMatchRenderedOutput('client');
});

// @gate !(enableUseRefAccessWarning && __DEV__)
test('Using isEqual to bailout', async () => {
const store = createExternalStore({a: 0, b: 0});

function A() {
const {a} = useSyncExternalStoreExtra(
const {a} = useSyncExternalStoreWithSelector(
store.subscribe,
store.getState,
null,
@@ -140,7 +135,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
return <Text text={'A' + a} />;
}
function B() {
const {b} = useSyncExternalStoreExtra(
const {b} = useSyncExternalStoreWithSelector(
store.subscribe,
store.getState,
null,
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
'use strict';

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

// Remove the built-in API from the React exports to force the package to
// use the shim.
if (!gate(flags => flags.supportsNativeUseSyncExternalStore)) {
// and the non-variant tests for the shim.
//
if (gate(flags => flags.enableUseSyncExternalStoreShim)) {
// Remove useSyncExternalStore from the React imports so that we use the
// shim instead. Also removing startTransition, since we use that to
// detect outdated 18 alphas that don't yet include useSyncExternalStore.
@@ -42,8 +38,6 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
startTransition: _,
// eslint-disable-next-line no-unused-vars
useSyncExternalStore: __,
// eslint-disable-next-line no-unused-vars
unstable_useSyncExternalStore: ___,
...otherExports
} = jest.requireActual('react');
return otherExports;
@@ -64,10 +58,21 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
// in both concurrent and legacy mode, I'm adding batching here.
act = cb => internalAct(() => ReactDOM.unstable_batchedUpdates(cb));

useSyncExternalStore = require('use-sync-external-store')
if (gate(flags => flags.source)) {
// The `shim/with-selector` module composes the main
// `use-sync-external-store` entrypoint. In the compiled artifacts, this
// is resolved to the `shim` implementation by our build config, but when
// running the tests against the source files, we need to tell Jest how to
// resolve it. Because this is a source module, this mock has no affect on
// the build tests.
jest.mock('use-sync-external-store/src/useSyncExternalStore', () =>
jest.requireActual('use-sync-external-store/shim'),
);
}
useSyncExternalStore = require('use-sync-external-store/shim')
.useSyncExternalStore;
useSyncExternalStoreExtra = require('use-sync-external-store/extra')
.useSyncExternalStoreExtra;
useSyncExternalStoreWithSelector = require('use-sync-external-store/shim/with-selector')
.useSyncExternalStoreWithSelector;
});

function Text({text}) {
@@ -78,7 +83,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
function createRoot(container) {
// This wrapper function exists so we can test both legacy roots and
// concurrent roots.
if (gate(flags => flags.supportsNativeUseSyncExternalStore)) {
if (gate(flags => !flags.enableUseSyncExternalStoreShim)) {
// The native implementation only exists in 18+, so we test using
// concurrent mode. To test the legacy root behavior in the native
// implementation (which is supported in the sense that it needs to have
@@ -265,7 +270,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {

// In React 18, you can't observe in between a sync render and its
// passive effects, so this is only relevant to legacy roots
// @gate !supportsNativeUseSyncExternalStore
// @gate enableUseSyncExternalStoreShim
test(
"compares to current state before bailing out, even when there's a " +
'mutation in between the sync and passive effects',
@@ -547,7 +552,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
await act(() => {
store.set({value: 1, throwInGetSnapshot: true, throwInIsEqual: false});
});
if (gate(flags => flags.supportsNativeUseSyncExternalStore)) {
if (gate(flags => !flags.enableUseSyncExternalStoreShim)) {
expect(Scheduler).toHaveYielded([
'Error in getSnapshot',
// In a concurrent root, React renders a second time to attempt to
@@ -595,7 +600,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {

function App() {
Scheduler.unstable_yieldValue('App');
const a = useSyncExternalStoreExtra(
const a = useSyncExternalStoreWithSelector(
store.subscribe,
store.getState,
null,
@@ -632,7 +637,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const store = createExternalStore({a: 0, b: 0});

function A() {
const {a} = useSyncExternalStoreExtra(
const {a} = useSyncExternalStoreWithSelector(
store.subscribe,
store.getState,
null,
@@ -642,7 +647,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
return <Text text={'A' + a} />;
}
function B() {
const {b} = useSyncExternalStoreExtra(
const {b} = useSyncExternalStoreWithSelector(
store.subscribe,
store.getState,
null,
@@ -711,7 +716,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
container.innerHTML = '<div>server</div>';
const serverRenderedDiv = container.getElementsByTagName('div')[0];

if (gate(flags => flags.supportsNativeUseSyncExternalStore)) {
if (gate(flags => !flags.enableUseSyncExternalStoreShim)) {
act(() => {
ReactDOM.hydrateRoot(container, <App />);
});
@@ -774,7 +779,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
Scheduler.unstable_yieldValue('Inline selector');
return [...state.items, 'C'];
};
const items = useSyncExternalStoreExtra(
const items = useSyncExternalStoreWithSelector(
store.subscribe,
store.getState,
null,
@@ -842,7 +847,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const selector = state => state.a.toUpperCase();

function App() {
const a = useSyncExternalStoreExtra(
const a = useSyncExternalStoreWithSelector(
store.subscribe,
store.getState,
null,
@@ -877,7 +882,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const isEqual = (left, right) => left.a.trim() === right.a.trim();

function App() {
const a = useSyncExternalStoreExtra(
const a = useSyncExternalStoreWithSelector(
store.subscribe,
store.getState,
null,
Original file line number Diff line number Diff line change
@@ -35,8 +35,6 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
startTransition: _,
// eslint-disable-next-line no-unused-vars
useSyncExternalStore: __,
// eslint-disable-next-line no-unused-vars
unstable_useSyncExternalStore: ___,
...otherExports
} = jest.requireActual('react');
return otherExports;
@@ -47,7 +45,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
ReactDOMServer = require('react-dom/server');
Scheduler = require('scheduler');

useSyncExternalStore = require('use-sync-external-store')
useSyncExternalStore = require('use-sync-external-store/shim')
.useSyncExternalStore;
});

Original file line number Diff line number Diff line change
@@ -7,6 +7,4 @@
* @flow
*/

'use strict';

export * from './src/useSyncExternalStoreExtra';
export const isServerEnvironment = false;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

'use strict';

// Intentionally not using named imports because Rollup uses dynamic
// dispatch for CommonJS interop named imports.
import * as React from 'react';

export const useSyncExternalStore = React.useSyncExternalStore;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

'use strict';

// Intentionally not using named imports because Rollup uses dynamic
// dispatch for CommonJS interop named imports.
import * as shim from 'use-sync-external-store/shim';

export const useSyncExternalStore = shim.useSyncExternalStore;
12 changes: 12 additions & 0 deletions packages/use-sync-external-store/src/isServerEnvironment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {canUseDOM} from 'shared/ExecutionEnvironment';

export const isServerEnvironment = !canUseDOM;
28 changes: 18 additions & 10 deletions packages/use-sync-external-store/src/useSyncExternalStore.js
Original file line number Diff line number Diff line change
@@ -7,16 +7,24 @@
* @flow
*/

import {canUseDOM} from 'shared/ExecutionEnvironment';
import {useSyncExternalStore as client} from './useSyncExternalStoreClient';
import {useSyncExternalStore as server} from './useSyncExternalStoreServer';
'use strict';

// Intentionally not using named imports because Rollup uses dynamic
// dispatch for CommonJS interop named imports.
import * as React from 'react';

const {unstable_useSyncExternalStore: builtInAPI} = React;
export const useSyncExternalStore = React.useSyncExternalStore;

export const useSyncExternalStore =
builtInAPI !== undefined
? ((builtInAPI: any): typeof client)
: canUseDOM
? client
: server;
if (__DEV__) {
console.error(
"The main 'use-sync-external-store' entry point is not supported; all it " +
"does is re-export useSyncExternalStore from the 'react' package, so " +
'it only works with React 18+.' +
'\n\n' +
'If you wish to support React 16 and 17, import from ' +
"'use-sync-external-store/shim' instead. It will fall back to a shimmed" +
'implementation when the native one is not available.' +
'\n\n' +
"If you only support React 18+, you can import directly from 'react'.",
);
}
18 changes: 18 additions & 0 deletions packages/use-sync-external-store/src/useSyncExternalStoreShim.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {useSyncExternalStore as client} from './useSyncExternalStoreShimClient';
import {useSyncExternalStore as server} from './useSyncExternalStoreShimServer';
import {isServerEnvironment} from './isServerEnvironment';
import {useSyncExternalStore as builtInAPI} from 'react';

const shim = isServerEnvironment ? server : client;

export const useSyncExternalStore =
builtInAPI !== undefined ? ((builtInAPI: any): typeof shim) : shim;
Original file line number Diff line number Diff line change
@@ -30,10 +30,10 @@ let didWarnUncachedGetSnapshot = false;
export function useSyncExternalStore<T>(
subscribe: (() => void) => () => void,
getSnapshot: () => T,
// Note: The client shim does not use getServerSnapshot, because pre-18
// versions of React do not expose a way to check if we're hydrating. So
// users of the shim will need to track that themselves and return the
// correct value from `getSnapshot`.
// Note: The shim does not use getServerSnapshot, because pre-18 versions of
// React do not expose a way to check if we're hydrating. So users of the shim
// will need to track that themselves and return the correct value
// from `getSnapshot`.
getServerSnapshot?: () => T,
): T {
if (__DEV__) {
Original file line number Diff line number Diff line change
@@ -12,5 +12,9 @@ export function useSyncExternalStore<T>(
getSnapshot: () => T,
getServerSnapshot?: () => T,
): T {
// Note: The shim does not use getServerSnapshot, because pre-18 versions of
// React do not expose a way to check if we're hydrating. So users of the shim
// will need to track that themselves and return the correct value
// from `getSnapshot`.
return getSnapshot();
}
Original file line number Diff line number Diff line change
@@ -9,14 +9,14 @@

import * as React from 'react';
import is from 'shared/objectIs';
import {useSyncExternalStore} from 'use-sync-external-store';
import {useSyncExternalStore} from 'use-sync-external-store/src/useSyncExternalStore';

// Intentionally not using named imports because Rollup uses dynamic
// dispatch for CommonJS interop named imports.
// Intentionally not using named imports because Rollup uses dynamic dispatch
// for CommonJS interop.
const {useRef, useEffect, useMemo, useDebugValue} = React;

// Same as useSyncExternalStore, but supports selector and isEqual arguments.
export function useSyncExternalStoreExtra<Snapshot, Selection>(
export function useSyncExternalStoreWithSelector<Snapshot, Selection>(
subscribe: (() => void) => () => void,
getSnapshot: () => Snapshot,
getServerSnapshot: void | null | (() => Snapshot),
12 changes: 12 additions & 0 deletions packages/use-sync-external-store/with-selector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

'use strict';

export {useSyncExternalStoreWithSelector} from 'use-sync-external-store/src/useSyncExternalStoreWithSelector';
5 changes: 2 additions & 3 deletions scripts/jest/TestFlags.js
Original file line number Diff line number Diff line change
@@ -84,9 +84,8 @@ function getTestFlags() {
source: !process.env.IS_BUILD,
www,

// This isn't a flag, just a useful alias for tests. Remove once
// useSyncExternalStore lands in the `next` channel.
supportsNativeUseSyncExternalStore: __EXPERIMENTAL__ || www,
// This isn't a flag, just a useful alias for tests.
enableUseSyncExternalStoreShim: !__VARIANT__,

// If there's a naming conflict between scheduler and React feature flags, the
// React ones take precedence.
7 changes: 7 additions & 0 deletions scripts/jest/config.build.js
Original file line number Diff line number Diff line change
@@ -45,6 +45,13 @@ packages.forEach(name => {
] = `<rootDir>/build/${NODE_MODULES_DIR}/${name}/$1`;
});

moduleNameMapper[
'use-sync-external-store/shim/with-selector'
] = `<rootDir>/build/${NODE_MODULES_DIR}/use-sync-external-store/shim/with-selector`;
moduleNameMapper[
'use-sync-external-store/shim/index.native'
] = `<rootDir>/build/${NODE_MODULES_DIR}/use-sync-external-store/shim/index.native`;

module.exports = Object.assign({}, baseConfig, {
// Redirect imports to the compiled bundles
moduleNameMapper,
44 changes: 33 additions & 11 deletions scripts/rollup/bundles.js
Original file line number Diff line number Diff line change
@@ -789,7 +789,7 @@ const bundles = [
externals: ['react'],
},

/******* Shim for useSyncExternalStore *******/
/******* useSyncExternalStore *******/
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: ISOMORPHIC,
@@ -800,26 +800,48 @@ const bundles = [
externals: ['react'],
},

/******* Shim for useSyncExternalStore (+ extra user-space features) *******/
/******* useSyncExternalStore (shim) *******/
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: ISOMORPHIC,
entry: 'use-sync-external-store/extra',
global: 'useSyncExternalStoreExtra',
minifyWithProdErrorCodes: true,
entry: 'use-sync-external-store/shim',
global: 'useSyncExternalStore',
minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: true,
externals: ['react', 'use-sync-external-store'],
externals: ['react'],
},

/******* Shim for useSyncExternalStore ReactNative *******/
/******* useSyncExternalStore (shim, native) *******/
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: ISOMORPHIC,
entry: 'use-sync-external-store/index.native',
global: 'useSyncExternalStoreNative',
minifyWithProdErrorCodes: true,
entry: 'use-sync-external-store/shim/index.native',
global: 'useSyncExternalStore',
minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: true,
externals: ['react', 'ReactNativeInternalFeatureFlags'],
externals: ['react'],
},

/******* useSyncExternalStoreWithSelector *******/
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: ISOMORPHIC,
entry: 'use-sync-external-store/with-selector',
global: 'useSyncExternalStoreWithSelector',
minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: true,
externals: ['react'],
},

/******* useSyncExternalStoreWithSelector (shim) *******/
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: ISOMORPHIC,
entry: 'use-sync-external-store/shim/with-selector',
global: 'useSyncExternalStoreWithSelector',
minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: true,
externals: ['react', 'use-sync-external-store/shim'],
},

/******* React Scheduler (experimental) *******/
18 changes: 18 additions & 0 deletions scripts/rollup/forks.js
Original file line number Diff line number Diff line change
@@ -481,6 +481,24 @@ const forks = Object.freeze({
return null;
}
},

'use-sync-external-store/src/useSyncExternalStore': (bundleType, entry) => {
if (entry.startsWith('use-sync-external-store/shim')) {
return 'use-sync-external-store/src/forks/useSyncExternalStore.forward-to-shim';
}
if (entry !== 'use-sync-external-store') {
// Internal modules that aren't shims should use the native API from the
// react package.
return 'use-sync-external-store/src/forks/useSyncExternalStore.forward-to-built-in';
}
return null;
},

'use-sync-external-store/src/isServerEnvironment': (bundleType, entry) => {
if (entry.endsWith('.native')) {
return 'use-sync-external-store/src/forks/isServerEnvironment.native';
}
},
});

module.exports = forks;
2 changes: 2 additions & 0 deletions scripts/shared/pathsByLanguageVersion.js
Original file line number Diff line number Diff line change
@@ -11,6 +11,8 @@ const esNextPaths = [
// Internal forwarding modules
'packages/*/*.js',
'packages/*/esm/*.js',
'packages/use-sync-external-store/shim/**/*.js',
'packages/use-sync-external-store/with-selector/**/*.js',
// Source files
'packages/*/src/**/*.js',
'packages/dom-event-testing-library/**/*.js',