localStorage-persisted context for your React apps, accessible through Hooks
UNLICENSE License
npm install react-local-store
import React from 'react';
import ReactDOM from 'react-dom';
import { LocalStoreProvider, useLocalStore } from 'react-local-store';
function App() {
const [state, dispatch] = useLocalStore();
return (
<div>
<h1>{state.title}</h1>
<input
type="text"
defaultValue={state.title}
onChange={event =>
dispatch({ type: 'UPDATE_TITLE', payload: event.target.value })
}
/>
</div>
);
}
ReactDOM.render(
<LocalStoreProvider
initialState={{
title: 'react-local-store'
}}
reducer={(state, action) => {
switch (action.type) {
case 'UPDATE_TITLE':
return { ...state, title: action.payload };
default:
return state;
}
}}
>
<App />
</LocalStoreProvider>,
document.getElementById('root')
);
Want to take this code for a spin right now? Glitch has got you covered. Hit that button down below to fork the example above and have a play around:
Once you're in there, give this a try:
<LocalStoreProvider />
and useLocalStore()
Provide global state to your entire app, enabled React's context API, then access (and update) it using Hooks
import React from 'react';
import ReactDOM from 'react-dom';
import { LocalStoreProvider, useLocalStore } from 'react-local-store';
const ACTION_TYPES = { INCREMENT: 'INCREMENT' };
function reducer(state, action) {
switch (action.type) {
case ACTION_TYPES.INCREMENT:
return { ...state, count: state.count + 1 };
default:
return state;
}
}
function App() {
const [state, dispatch] = useLocalStore();
return (
<button onClick={() => dispatch({ type: ACTION_TYPES.INCREMENT })}>
{state.count}
</button>
);
}
ReactDOM.render(
<LocalStoreProvider initialState={{ count: 0 }} reducer={reducer}>
<App />
</LocalStoreProvider>,
document.getElementById('root')
);
By default, global state change listeners are used (using window.requestIdleCallback
or a simple polyfill) so that changes to your store trigger a re-render in every app instance, including those in other browser tabs. If you want to disable the listener, set the sync
prop to false
in your Provider:
<LocalStoreProvider sync={false}>
<App />
</LocalStoreProvider>
By default your state will be persisted to localStorage
under the key: __REACT_LOCAL_STORE__
. If you want to have multiple stores (or use something other than the default), you have a couple of options. The first is to name your stores with LocalStoreProvider
's name
prop and useLocalStore
's optional argument:
/* ... */
function App() {
const [state, dispatch] = useLocalStore('custom-store-name');
/* ... */
}
ReactDOM.render(
<LocalStoreProvider name="custom-store-name" initialState={...} reducer={...}>
<App />
</LocalStoreProvider>,
document.getElementById('root')
);
The (arguably better) alternative is to use the createLocalStore
factory...
createLocalStore()
Writing custom store names across various components in different files can start to get a bit tedious, and isn't very DRY, so you have the option of creating your own preset Providers and Hooks, with the createLocalStore
factory.
In store.js
:
import { createLocalStore } from 'react-local-store';
const ACTION_TYPES = { INCREMENT: 'INCREMENT' };
function reducer(state, action) {
switch (action.type) {
case ACTION_TYPES.INCREMENT:
return { ...state, count: state.count + 1 };
default:
return state;
}
}
const [LocalStoreProvider, useLocalStore] = createLocalStore({
name: 'custom-store-name',
initialState: { count: 0 },
reducer
});
export { ACTION_TYPES, LocalStoreProvider, useLocalStore };
In index.js
:
import React from 'react';
import ReactDOM from 'react-dom';
import { ACTION_TYPES, LocalStoreProvider, useLocalStore } from './store';
function App() {
const [state, dispatch] = useLocalStore();
return (
<button onClick={() => dispatch({ type: ACTION_TYPES.INCREMENT })}>
{state.count}
</button>
);
}
ReactDOM.render(
<LocalStoreProvider>
<App />
</LocalStoreProvider>,
document.getElementById('root')
);
Any props you omit when creating your custom store will be expected when you use it. For example, you can create a custom store, only specifying the name
, and still supply your own initialState
, reducer
and (optionally) sync
props when creating Provider instances.
<16.8
)localStorage
is 😴)react-local-store
is currently maintained by Colin Gourlay