React hooks for persistent state
Custom React hooks for keeping application state in sync with
localStorageor
sessionStorage.
:book: Familiar API. You already know how to use this library! Replace
useStateand
useReducerhooks with the ones in this library and get persistent state for free.
:sparkles: Fully featured. Automatically stringifies and parses values coming and going to storage, keeps state in sync between tabs by listening to storage events and handles non-straightforward use cases correctly.
:zap: Tiny and fast. Less than 700 bytes gzipped, enforced with
size-limit. No external dependencies. Only reads from storage when necessary and writes to storage after rendering.
:capital_abcd: Completely typed. Written in TypeScript. Type definitions included and verified with
tsd.
:muscle: Backed by tests. Full coverage of the API.
You need to use version 16.8.0 or greater of React, since that's the first one to include hooks. If you still need to create your application, Create React App is the officially supported way.
Add the package to your React project:
npm install --save react-storage-hooks
Or with yarn:
yarn add react-storage-hooks
The
useStorageStateand
useStorageReducerhooks included in this library work like
useStateand
useReducer. The only but important differences are:
Storageobject (
localStorageor
sessionStorage) and storage key.
undefined, and will be updated with
Errorobjects thrown by
Storage.setItem. However the hook will keep updating state even if new values fail to be written to storage, to ensure that your application doesn't break.
useStorageState
import React from 'react'; import { useStorageState } from 'react-storage-hooks';function StateCounter() { const [count, setCount, writeError] = useStorageState( localStorage, 'state-counter', 0 );
return ( <>
You clicked {count} times
setCount(count + 1)}>+ setCount(count - 1)}>- {writeError && (Cannot write to localStorage: {writeError.message})} > ); }
function useStorageState( storage: Storage, key: string, defaultState?: S | (() => S) ): [S, React.Dispatch>, Error | undefined];
useStorageReducer
import React from 'react'; import { useStorageReducer } from 'react-storage-hooks';function reducer(state, action) { switch (action.type) { case 'inc': return { count: state.count + 1 }; case 'dec': return { count: state.count - 1 }; default: return state; } }
function ReducerCounter() { const [state, dispatch, writeError] = useStorageReducer( localStorage, 'reducer-counter', reducer, { count: 0 } );
return ( <>
You clicked {state.count} times
dispatch({ type: 'inc' })}>+ dispatch({ type: 'dec' })}>- {writeError && (Cannot write to localStorage: {writeError.message})} > ); }
function useStorageReducer( storage: Storage, key: string, reducer: React.Reducer, defaultState: S ): [S, React.Dispatch, Error | undefined];
function useStorageReducer( storage: Storage, key: string, reducer: React.Reducer, defaultInitialArg: I, defaultInit: (defaultInitialArg: I) => S ): [S, React.Dispatch, Error | undefined];
The
storageparameter of the hooks can be any object that implements the
getItem,
setItemand
removeItemmethods of the
Storageinterface. Keep in mind that storage values will be automatically serialized and parsed before and after calling these methods.
interface Storage { getItem(key: string): string | null; setItem(key: string, value: string): void; removeItem(key: string): void; }
This library checks for the existence of the
windowobject and even has some tests in a node-like environment. However in your server code you will need to provide a storage object to the hooks that works server-side. A simple solution is to use a dummy object like this:
const dummyStorage = { getItem: () => null, setItem: () => {}, removeItem: () => {}, };
The important bit here is to have the
getItemmethod return
null, so that the default state parameters of the hooks get applied as initial state.
If you're using a few hooks in your application with the same type of storage, it might bother you to have to specify the storage object all the time. To alleviate this, you can write a custom hook like this:
import { useStorageState } from 'react-storage-hooks';export function useLocalStorageState(...args) { return useStorageState(localStorage, ...args); }
And then use it in your components:
import { useLocalStorageState } from './my-hooks';function Counter() { const [count, setCount] = useLocalStorageState('counter', 0);
// Rest of the component }
Install development dependencies:
npm install
To set up the examples:
npm run examples:setup
To start a server with the examples in watch mode (reloads whenever examples or library code change):
npm run examples:watch
Run tests:
npm test
Run tests in watch mode:
npm run test:watch
See code coverage information:
npm run test:coverage
Go to the
masterbranch:
git checkout master
Bump the version number:
npm version [major | minor | patch]
Run the release script:
npm run release
All code quality checks will run, the tagged commit generated by
npm versionwill be pushed and Travis CI will publish the new package version to the npm registry.
This library is MIT licensed.