react-local-store
v3.1.0
Published
localStorage-persisted context for your React apps, accessible through Hooks
Downloads
21
Maintainers
Readme
Table of contents
Getting started
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:
- Change the value in the field and see that the heading also updates.
- Refresh the page and see that your state was persisted.
- Open the app in another tab to see that its context is not only shared, but synchronised with the current tab.
API
<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')
);
Synced global state
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>
Custom store names
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.
Reasons to not use this
- You're using a pre-Hooks version of React (
<16.8
) - You'll be using state that can't be serialised to JSON (i.e. functions)
- You update state often in short time periods (
localStorage
is 😴) - You want to access state outside of functional components
- You don't want to use a reducer to modify state (check out context-storage instead)
About the project
react-local-store
is currently maintained by Colin Gourlay- It is currently licensed under The Unlicense
- If you'd like to help out, please submit ideas & bugs to the project's issue tracker
- To contribute code and documentation, please see the contribution guide
- All contributions and project activity are subject to the project's code of conduct