diff --git a/README.md b/README.md index ba50b47..68cedf7 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,17 @@ persist('some', someStore, { - **jsonify** *bool* Enables serialization as JSON (default: `true`). - **whitelist** *Array\* Only these keys will be persisted (defaults to all keys). - **blacklist** *Array\* These keys will not be persisted (defaults to all keys). + - **nodeNoop** *bool* Whether this should no-op in a Node environment (default: `true`). See below for more details. - returns a void Promise +### Node and SSR Usage + +To support Server-Side Rendering (SSR) out-of-the-box, `persist` will no-op in a Node environment by default.
+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. + +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. + ## Examples 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. diff --git a/src/index.ts b/src/index.ts index c9f4e33..05d0ce9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,16 +9,32 @@ export interface IOptions { storage?: any, jsonify?: boolean, readonly whitelist?: Array, - readonly blacklist?: Array + readonly blacklist?: Array, + nodeNoop?: boolean } type StrToAnyMap = {[key: string]: any} export const persist: IArgs = (name, store, options = {}) => { - let {storage, jsonify, whitelist, blacklist} = options + let {storage, jsonify, whitelist, blacklist, nodeNoop} = options - if (typeof window.localStorage !== 'undefined' && (!storage || storage === window.localStorage)) { + // no-op in node by default to support SSR out-of-the-box + // require explicit opt-in to hydrate server-side + if (!nodeNoop) { nodeNoop = true } + if (nodeNoop && typeof window === 'undefined') { return Promise.resolve() } + + // use AsyncLocalStorage by default or if window.localStorage was passed in + if ( + typeof window !== 'undefined' && + typeof window.localStorage !== 'undefined' && + (!storage || storage === window.localStorage) + ) { storage = AsyncLocalStorage } + if (!storage) { + return Promise.reject('localStorage (the default) is not supported in ' + + 'this environment, please configure a supported storage provider') + } + if (!jsonify) { jsonify = true } // default to true like mobx-persist const whitelistDict = arrToDict(whitelist) const blacklistDict = arrToDict(blacklist)