diff --git a/docs/utilities/storage.mdx b/docs/utilities/storage.mdx index b0c9bdb1a6..56e758b85e 100644 --- a/docs/utilities/storage.mdx +++ b/docs/utilities/storage.mdx @@ -49,6 +49,26 @@ If not specified, the default storage implementation uses `localStorage` for sto +### `createJSONStorage` util + +To create a custom storage implementation with `JSON.stringify()`/`JSON.parse()` for the `storage` option, `createJSONStorage` util is provided. + +Usage: + +```js +const storage = createJSONStorage( + // getStringStorage + () => localStorage, // or sesseionStorage, asyncStorage or alike + // options (optional) + { + reviver, // optional reviver option for JSON.parse + replacer, // optional replacer option for JSON.stringify + }, +) +``` + +Note: `JSON.parse` is not type safe. If it can't accept any types, some kind of validation would be necessary for production apps. + ### Server-side rendering Any JSX markup that depends on the value of a stored atom (e.g., a `className` or `style` prop) will use the `initialValue` when rendered on the server (since `localStorage` and `sessionStorage` are not available on the server). diff --git a/src/vanilla/utils/atomWithStorage.ts b/src/vanilla/utils/atomWithStorage.ts index b07ff454b7..147eeab606 100644 --- a/src/vanilla/utils/atomWithStorage.ts +++ b/src/vanilla/utils/atomWithStorage.ts @@ -46,16 +46,23 @@ export interface SyncStringStorage { removeItem: (key: string) => void } +type JsonStorageOptions = { + reviver?: (key: string, value: unknown) => unknown + replacer?: (key: string, value: unknown) => unknown +} export function createJSONStorage( getStringStorage: () => AsyncStringStorage, + options?: JsonStorageOptions, ): AsyncStorage export function createJSONStorage( getStringStorage: () => SyncStringStorage, + options?: JsonStorageOptions, ): SyncStorage export function createJSONStorage( getStringStorage: () => AsyncStringStorage | SyncStringStorage | undefined, + options?: JsonStorageOptions, ): AsyncStorage | SyncStorage { let lastStr: string | undefined let lastValue: any @@ -65,7 +72,7 @@ export function createJSONStorage( str = str || '' if (lastStr !== str) { try { - lastValue = JSON.parse(str) + lastValue = JSON.parse(str, options?.reviver) } catch { return initialValue } @@ -80,7 +87,10 @@ export function createJSONStorage( return parse(str) }, setItem: (key, newValue) => - getStringStorage()?.setItem(key, JSON.stringify(newValue)), + getStringStorage()?.setItem( + key, + JSON.stringify(newValue, options?.replacer), + ), removeItem: (key) => getStringStorage()?.removeItem(key), } if (