useStorage
function useStorage<T>(options: StorageOptions<T>): StorageImplementation<T>;
The useStorage
hook can be used to control a single value and persist it
to localStorage.
Example Usage
The useStorage
hook can be used to read and write from localStorage
(default) or sessionStorage
. The default behavior will automatically sync
the value across tabs using the StorageEvent
.
import { TextField } from "@react-md/core/form/TextField";
import { useStorage } from "@react-md/core/storage/useStorage";
function Example() {
const { value, setValue } = useStorage({
key: "savedSearch",
defaultValue: "",
});
return (
<TextField
label="Search"
placeholder="Search..."
type="search"
value={value}
onChange={(event) => {
setValue(event.currentTarget.value);
}}
/>
);
}
Type-safe Objects
The value
can be type-safe by providing the value as a type parameter and a
deserializer
function that validates the value in local storage.
import { useStorage } from "@react-md/core/storage/useStorage";
import { type ReactElement } from "react";
interface ExpectedSchema {
label: string;
value: string;
// others
}
function Example(): ReactElement {
const { value, setValue } = useStorage<ExpectedSchema | null>({
key: "someKey",
defaultValue: null,
// this is optional: you can create a custom deserializer to validate
// the stored value to prevent people manually updating local storage in
// the dev tools
deserializer(item) {
const parsed = JSON.parse(item);
const { label, value } = parsed;
if (typeof label !== "string" || typeof value !== "string") {
return null;
}
return { label, value };
},
});
// do something
// value will be `ExpectedSchema | null`
}
Manual Persistence
The useStorage
hook defaults to automatically saving the current value
in local storage immediately. To allow manual persisting to local storage,
enable the manual
option and use the persist()
or remove()
functions.
import { Button } from "@react-md/core/button/Button";
import { Checkbox } from "@react-md/core/form/Checkbox";
import { Form } from "@react-md/core/form/Form";
import { useStorage } from "@react-md/core/storage/useStorage";
import { type ReactElement } from "react";
function Example(): ReactElement {
const { value, setValue, remove, persist } = useStorage({
key: "someKey",
manual: true,
defaultValue: false,
});
return (
<Form
onSubmit={() => {
// current value will be saved into local storage
persist();
}}
onReset={() => {
// "someKey" will be removed from local storage
remove();
}}
>
<Checkbox
label="Allow cookies"
checked={value}
onChange={(event) => {
setValue(event.currentTarget.checked);
}}
/>
<Button type="reset">Decline</Button>
<Button type="submit">Save</Button>
</Form>
);
}
Parameters
options
- An object with the following definition:
export interface StorageOptions<T> {
/**
* The storage key name to use. This can be set to an empty string for
* internal usage of conditionally saving items to storage.
*/
key: string;
/**
* The default value to use if an item does not exist in storage.
*/
defaultValue: UseStateInitializer<T>;
/**
* Set this to `true` to update:
*
* - the default {@link serializer} to be:
* ```
* typeof value === "string" ? value : `${value}`
* ```
* - the default {@link deserializer} to not call `JSON.parse` if the
* {@link defaultValue} is a string.
*
* @defaultValue `typeof defaultValue === 'string'`
*/
raw?: boolean;
/**
* Set this to `true` to require a manual `persist()` or `remove()` call
* to update the storage value.
*
* @defaultValue `false`
*/
manual?: boolean;
/** @defaultValue `globalThis.localStorage` */
storage?: Storage;
/**
* An optional function to serialize the `value` before storing it in local
* storage.
*
* @defaultValue `JSON.stringify`
*/
serializer?: (value: T) => string;
/**
* An optional function to deserialize the `value` if the item existed in
* local storage.
*
* @defaultValue `JSON.parse`
*/
deserializer?: (item: string) => T;
}
Returns
export interface StorageImplementation<T> {
value: T;
/**
* Updates the {@link value} in state. When the
* {@link StorageOptions.manual} option is `false`, the value will
* also be updated in local storage immediately.
*/
setValue: UseStateSetter<T>;
/**
* Remove the item from local storage.
*/
remove: () => void;
/**
* Manually persist the current {@link value} into local storage. This is only
* useful if the {@link StorageOptions.manual} option is `true`.
*
* @example Manual Persisting
* ```tsx
* import type { ReactElement } from "react";
*
* function Example(): ReactElement {
* const { value, setValue, persist } = useStorage({
* key: "someKey",
* manual: true,
* defaultValue: "",
* });
*
* return (
* <>
* <Button onClick={closeDialog}>
* Cancel
* </Button>
* <Button
* onClick={async () => {
* await saveToDatabase(value);
* persist();
* closeDialog();
* }}
* >
* Confirm
* </Button>
* </>
* );
* }
* ```
*/
persist: () => void;
}
Utils
getItemFromStorage
function getItemFromStorage<T>(options: GetItemFromStorageOptions<T>): T;
This is a low-level helper function get a value from storage (defaults to
localStorage
). You'll most likely want to use a pre-built implementation
like useStorage
instead.
Example Usage
import { getItemFromStorage } from "@react-md/core/storage/utils";
const values = ["a", "b", "c", "d"] as const;
const item1 = getItemFromStorage({
key: "testKey",
fallback: values[0],
deserializer(item) {
if (!values.includes(item)) {
return values[0];
}
return item;
},
});
const item2 = getItemFromStorage({
key: "anotherKey",
fallback: -1,
});
const item3 = getItemFromStorage({
key: "anotherKey",
fallback: -1,
storage: sessionStorage,
});
Parameters
options
- An object with the following definition:
export type ModifyStorageOptions = Pick<
StorageOptions<unknown>,
"key" | "storage"
>;
export interface GetItemFromStorageOptions<T> extends ModifyStorageOptions {
/**
* A value to use when the {@link key} does not exist in storage or there is
* an error deserializing the value.
*/
fallback: T;
/** @see {@link StorageOptions.deserializer} */
deserializer?: StorageDeserializer<T>;
}
Returns
The type-safe value.
setItemInStorage
function setItemInStorage<T>(options: SetItemInStorageOptions<T>): void;
You'll most likely want to use useStorage
instead, but this is a low-level
util to "safely" set an item in localStorage
or sessionStorage
.
Example Usage
import { getItemFromStorage } from "@react-md/core/storage/utils";
import { identity } from "@react-md/core/utils/identity";
const values = ["a", "b", "c", "d"] as const;
setItemInStorage({
key: "testKey",
value: values[0],
// store string value as-is
serializer: identity,
});
setItemInStorage({
key: "anotherKey",
value: 100,
});
setItemInStorage({
key: "anotherKey",
value: 100,
storage: sessionStorage,
});
Parameters
options
- An object with the following definition:
export type ModifyStorageOptions = Pick<
StorageOptions<unknown>,
"key" | "storage"
>;
export interface SetItemInStorageOptions<T> extends ModifyStorageOptions {
value: T;
/** @see {@link StorageOptions.serializer} */
serializer?: StorageSerializer<T>;
}
Returns
Nothing.