-
Notifications
You must be signed in to change notification settings - Fork 46.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement getServerSnapshot in userspace shim
If the DOM is not present, we assume that we are running in a server environment and return the result of `getServerSnapshot`. This heuristic doesn't work in React Native, so we'll need to provide a separate native build (using the `.native` extension). I've left this for a follow-up. We can't call `getServerSnapshot` on the client, because in versions of React before 18, there's no built-in mechanism to detect whether we're hydrating. To avoid a server mismatch warning, users must account for this themselves and return the correct value inside `getSnapshot`. Note that none of this is relevant to the built-in API that is being added in 18. This only affects the userspace shim that is provided for backwards compatibility with versions 16 and 17.
- Loading branch information
Showing
4 changed files
with
182 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShimServer-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/** | ||
* 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. | ||
* | ||
* @emails react-core | ||
* | ||
* @jest-environment node | ||
*/ | ||
|
||
'use strict'; | ||
|
||
let useSyncExternalStore; | ||
let React; | ||
let ReactDOM; | ||
let ReactDOMServer; | ||
let Scheduler; | ||
|
||
// This tests the userspace shim of `useSyncExternalStore` in a server-rendering | ||
// (Node) environment | ||
describe('useSyncExternalStore (userspace shim, server rendering)', () => { | ||
beforeEach(() => { | ||
jest.resetModules(); | ||
|
||
// 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. | ||
// | ||
// Longer term, we'll probably test this branch using an actual build of | ||
// React 17. | ||
jest.mock('react', () => { | ||
const { | ||
// eslint-disable-next-line no-unused-vars | ||
startTransition: _, | ||
// eslint-disable-next-line no-unused-vars | ||
useSyncExternalStore: __, | ||
// eslint-disable-next-line no-unused-vars | ||
unstable_useSyncExternalStore: ___, | ||
...otherExports | ||
} = jest.requireActual('react'); | ||
return otherExports; | ||
}); | ||
|
||
React = require('react'); | ||
ReactDOM = require('react-dom'); | ||
ReactDOMServer = require('react-dom/server'); | ||
Scheduler = require('scheduler'); | ||
|
||
useSyncExternalStore = require('use-sync-external-store') | ||
.useSyncExternalStore; | ||
}); | ||
|
||
function Text({text}) { | ||
Scheduler.unstable_yieldValue(text); | ||
return text; | ||
} | ||
|
||
function createExternalStore(initialState) { | ||
const listeners = new Set(); | ||
let currentState = initialState; | ||
return { | ||
set(text) { | ||
currentState = text; | ||
ReactDOM.unstable_batchedUpdates(() => { | ||
listeners.forEach(listener => listener()); | ||
}); | ||
}, | ||
subscribe(listener) { | ||
listeners.add(listener); | ||
return () => listeners.delete(listener); | ||
}, | ||
getState() { | ||
return currentState; | ||
}, | ||
getSubscriberCount() { | ||
return listeners.size; | ||
}, | ||
}; | ||
} | ||
|
||
test('basic server render', async () => { | ||
const store = createExternalStore('client'); | ||
|
||
function App() { | ||
const text = useSyncExternalStore( | ||
store.subscribe, | ||
store.getState, | ||
() => 'server', | ||
); | ||
return <Text text={text} />; | ||
} | ||
|
||
const html = ReactDOMServer.renderToString(<App />); | ||
expect(Scheduler).toHaveYielded(['server']); | ||
expect(html).toEqual('server'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters