Skip to content

Commit c141d03

Browse files
committed
(feat): support SSR out-of-the-box by no-op'ing in Node
- add a `nodeNoop` option that is `true` by default - generally folks do not want to hydrate their store during SSR, so no-op by default and require explicit opt-in to Node hydration - see #6 for discussion - also throw an Error if localStorage isn't supported and no alternative storage provider has been given (docs): add `nodeNoop` option and section on Node and SSR usage to give more details and note possible gotchas
1 parent 17a5b7a commit c141d03

File tree

2 files changed

+27
-3
lines changed

2 files changed

+27
-3
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,17 @@ persist('some', someStore, {
5353
- **jsonify** *bool* Enables serialization as JSON (default: `true`).
5454
- **whitelist** *Array\<string\>* Only these keys will be persisted (defaults to all keys).
5555
- **blacklist** *Array\<string\>* These keys will not be persisted (defaults to all keys).
56+
- **nodeNoop** *bool* Whether this should no-op in a Node environment (default: `true`). See below for more details.
5657

5758
- returns a void Promise
5859

60+
### Node and SSR Usage
61+
62+
To support Server-Side Rendering (SSR) out-of-the-box, `persist` will no-op in a Node environment by default.<br>
63+
Please note that it uses `typeof window === 'undefined'` to check. [`window` is defined in React Native](https://stackoverflow.com/questions/49911424/what-does-the-variable-window-represent-in-react-native), but may not be in test runners and other simulated environments.
64+
65+
If you'd like to hydrate your store in Node (vs. in the browser, which is the standard usage), set `nodeNoop` to `false` and `storage` to a supported provider for Node.
66+
5967
## Examples
6068

6169
None yet, but can take a look at [agilgur5/react-native-manga-reader-app](https://github.com/agilgur5/react-native-manga-reader-app) which uses it in production.

src/index.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,32 @@ export interface IOptions {
99
storage?: any,
1010
jsonify?: boolean,
1111
readonly whitelist?: Array<string>,
12-
readonly blacklist?: Array<string>
12+
readonly blacklist?: Array<string>,
13+
nodeNoop?: boolean
1314
}
1415
type StrToAnyMap = {[key: string]: any}
1516

1617
export const persist: IArgs = (name, store, options = {}) => {
17-
let {storage, jsonify, whitelist, blacklist} = options
18+
let {storage, jsonify, whitelist, blacklist, nodeNoop} = options
1819

19-
if (typeof window.localStorage !== 'undefined' && (!storage || storage === window.localStorage)) {
20+
// no-op in node by default to support SSR out-of-the-box
21+
// require explicit opt-in to hydrate server-side
22+
if (!nodeNoop) { nodeNoop = true }
23+
if (nodeNoop && typeof window === 'undefined') { return Promise.resolve() }
24+
25+
// use AsyncLocalStorage by default or if window.localStorage was passed in
26+
if (
27+
typeof window !== 'undefined' &&
28+
typeof window.localStorage !== 'undefined' &&
29+
(!storage || storage === window.localStorage)
30+
) {
2031
storage = AsyncLocalStorage
2132
}
33+
if (!storage) {
34+
return Promise.reject('localStorage (the default) is not supported in ' +
35+
'this environment, please configure a supported storage provider')
36+
}
37+
2238
if (!jsonify) { jsonify = true } // default to true like mobx-persist
2339
const whitelistDict = arrToDict(whitelist)
2440
const blacklistDict = arrToDict(blacklist)

0 commit comments

Comments
 (0)