Store enhancer that syncs (a subset) of your Redux store state to localstorage.

Overview

redux-localstorage

Store enhancer that syncs (a subset) of your Redux store state to localstorage.

NOTE: Be sure to check out the 1.0-breaking-changes branch (available on npm as [email protected]). It includes support for flexible storage backends, including (but not limited to) sessionStorage and react-natives' AsyncStorage.

Installation

npm install --save redux-localstorage

Usage

import {compose, createStore} from 'redux';
import persistState from 'redux-localstorage'

const enhancer = compose(
  /* [middlewares] */,
  persistState(/*paths, config*/),
)

const store = createStore(/*reducer, [initialState]*/, enhancer)

persistState(paths, config)

paths

type paths = Void | String | Array<String>

If left Void, persistState will sync Redux's complete store state with localStorage. Alternatively you may specify which part(s) of your state should be persisted.

Note: Currently no support for nested paths. Only "top-level" paths are supported, i.e. state[path]. If your needs are more complex and you require more control over which parts of your store's state should be persisted you can define your own strategy through config.slicer

config

config.key
type config.key = String

The localStorage key used to store state. The default value is redux.

config.slicer
type config.slicer = (paths: Any) => (state: Collection) => subset: Collection

Config.slicer allows you to define your own function which will be used to determine which parts should be synced with localStorage. It should look something like this:

function myCustomSlicer (paths) {
  return (state) => {
    let subset = {}
    /*Custom logic goes here*/
    return subset
  }
}

It is called with the paths argument supplied to persistState. It should return a function that will be called with the store's state, which should return a subset that matches the original shape/structure of the store - it's this subset that'll be persisted.

If, for example, you want to dynamically persist parts of your store state based on a user's preference, defining your own slicer allows you to do that. Simply add something along the following lines to your customSlicer function:

paths.forEach((path) => {
  if (state[path].persistToLocalStorage)
    subset[path] = state[path]
}

Immutable Data

If you're using immutable collections or some other custom collection, redux-localstorage exposes a number of functions that can be overridden by providing the following config options. These allow you to specify your own transformations based on your needs. If you're using ordinary javascript Objects, Arrays or primitives, you shouldn't have to concern yourself with these options.

config.serialize
type config.serialize = (subset: Collection) => serializedData: String

The default serialization strategy is JSON.stringify. Specifying a serialize function as part of your config will override this. This function receives a single argument (the subset of your store's state about to be persisted) and should return a serialized (i.e. stringified) representation thereof.

config.deserialize
type config.deserialize = (serializedData: String) => subset: Collection

The default deserialization strategy is JSON.parse. Specifying a deserialize function as part of your config will override this. This function receives a single argument (a serialized representation of your persisted state) and should return the data in a format that's expected by your application.

config.merge
type config.merge = (initialState: Collection, persistedState: Collection) => finalInitialState: Collection

During initialization any persisted state is merged with the initialState passed in as an argument to createStore. The default strategy extends the initialState with the persistedState. Override this function if that doesn't work for you. Note: this is only required if you want to merge values within an immutable collection. If your values are immutable, but the object that holds them is not, the default strategy should work just fine.

Comments
  • Async init convention

    Async init convention

    It doesn't appear as though there's a simple way to know whether the store has been mounted from localStorage.

    Would it make sense to add a reducer specific to the state of the adapter interaction which contained the current state: whether state has been loaded from the appropriate adapter, whether it has been initialized, if it is in the process of saving, etc.

    I'd like to wait until the state is mounted from the adapter before initializing any components in react-native, as I'm storing the navigation state in localstorage.

    Also wanted to know what you thought about adding a way to clear / reset the localstorage state on the top level store.

    opened by tgriesser 26
  • Handle localStorage if not defined (isomorphic)

    Handle localStorage if not defined (isomorphic)

    We get the following with the 1.0 breaking changes branch when firing up the server:

    /Users/whatevs/projects/stuff/ui/node_modules/redux-localstorage/lib/persistState.js:29
    var defaultStorage = (0, _adaptersLocalStorage2['default'])(localStorage);
                                                                ^
    ReferenceError: localStorage is not defined
        at Object.<anonymous> (/Users/whatevs/projects/stuff/ui/node_modules/redux-localstorage/lib/persistState.js:29:61)
        at Module._compile (module.js:456:26)
        at Object.Module._extensions..js (module.js:474:10)
        at Module.load (module.js:356:32)
        at Function.Module._load (module.js:312:12)
        at Module.require (module.js:364:17)
        at require (module.js:380:17)
        at Object.<anonymous> (/Users/whatevs/projects/stuff/ui/node_modules/redux-localstorage/lib/index.js:17:23)
        at Module._compile (module.js:456:26)
        at Object.Module._extensions..js (module.js:474:10)
     +407ms
    

    I presume this is b/c of the server-side rendering. So, I added this to fix it in persistState.js:

    if (typeof window === "undefined" ) {
      require('fs');
      var LocalStorage = require('node-localstorage').LocalStorage;
      localStorage = new LocalStorage('./scratch');
    } else {
      localStorage = window.localStorage;
    }
    // this is existing 
    var defaultStorage = (0, _adaptersLocalStorage2['default'])(localStorage);
    

    It adds a dependency on node-localstorage, but that doesn't seem too unreasonable...does it?

    Thoughts?

    opened by ruprict 24
  • How to merge slicer subset into initial state

    How to merge slicer subset into initial state

    I've defined a slicer to specifiy a subset which is saved to localStorage. Inside one of my reducers I've got an initial state defined, comparable to this example.

    const initialState = {
      list: [],
      favorites: [],
      isLoading: false
    }
    
    // the default value `initialState` won't be used anymore when there's 
    // already a subset of the initialstate define in localstorage
    export default function jobs (state = initialState, action) {
      const reduceFn = actionsMap[action.type]
      if (!reduceFn) return state
      return Object.assign({}, state, reduceFn(state, action))
    }
    

    My reduce won't consume the initial state anymore, since there's another initial state passed to this reducer, together with the action named "@@redux/INIT.

    How should I merge both the initialState and the state coming from local storage?

    opened by klaasman 21
  • Refine v1.0 APIs

    Refine v1.0 APIs

    A few thoughts:

    1. I don't see any reason to use this library with only persistState but not mergePersistedState, so it doesn't make sense to make persistState as _the_ default export. Please consider making it work like this instead:

      import {persistState, mergePersistedState} from 'redux-localstorage';
      
    2. It's not good to import something from a third-party library's inner directory structure because this creates an extra layer of code dependency. This will cause breaking changes when you simply wanna change your project's directory structure. For reference, React is also moving all the addons out of the main package into independent packages.

    3. It feels really weird that in case I have to include a filter (which is gonna be very common for real world cases), my related code would go from simply:

    import {compose, createStore} from 'redux';
    import {persistState} from 'redux-localstorage';
    
    compose(
      persistState()
    )(createStore)
    

    To this bloated mess:

    import {compose, createStore} from 'redux';
    import {persistState} from 'redux-localstorage';
    import adapter from 'redux-localstorage/lib/adapters/localStorage';
    import filter from 'redux-localstorage-filter';
    
    compose(
      persistState(
        compose(
          filter('nested.key')
        )(adapter(window.localStorage))
      )
    )(createStore)
    

    The weirdest part is - I don't understand why we have to create the localStorage storage ourselves. Shouldn't this be handled by the library which is called precisely redux-localstorage?

    If there is no justified benefit, couldn't we make it a bit simpler like this?

    import {compose, createStore} from 'redux';
    import {persistState, localStorage} from 'redux-localstorage';
    import filter from 'redux-localstorage-filter';
    
    compose(
      persistState(
        compose(
          filter('nested.key')
        )(localStorage)
      )
    )(createStore)
    

    Where the localStorage export is simply adapter(window.localStorage).

    opened by gsklee 13
  • Push v1 changes to NPM

    Push v1 changes to NPM

    @elgerlambert Per the discussion in #13, it would be really nice if you could push the changes to npm.

    Currently, I have to patch my version with the code from the PR.

    opened by hoodsy 11
  • persistState namespace

    persistState namespace

    redux-devtools currently uses persistState as a module, which conflicts with this module's persistState. Any way I can use both of these addons?

    Thanks

    opened by raineroviir 10
  • Using Immutable.js state with redux-localstorage

    Using Immutable.js state with redux-localstorage

    I'm not sure how to setup the config parameter to properly use an Immutable object, This is how I configure the options:

    import Immutable from 'immutable';
    
    export const LocalStorageConfig = {
        serialize: (subset) => subset.toJson(),
        deserialize: (serializedData) => Immutable.fromJS(JSON.parse(serializedData)),
        merge: (initialState, persistedState) => initialState.mergeDeep(persistedState)
    };
    

    But I get an error with my selector, state.get is not a function :

    export const menuSelector = createSelector(
        mainSelector,
            (state) => ({
                menu: state.get('menu')
        })
    );
    

    It looks like I'm missing something here.

    opened by localhosted 9
  • isomorphic support: localStorage is not defined

    isomorphic support: localStorage is not defined

    Failed to retrieve initialize state from localStorage: ReferenceError: localStorage is not defined
        at /Users/azz0r/Sites/mobile-presales/node_modules/redux-localstorage/lib/persistState.js:59:38
        at /Users/azz0r/Sites/mobile-presales/node_modules/redux/lib/applyMiddleware.js:38:19
        at createStore (/Users/azz0r/Sites/mobile-presales/node_modules/redux/lib/createStore.js:59:33)
    

    Any work arounds? This doesn't block redux-localstorage working but it leaves an initial error message in console.

    opened by azz0r 7
  • App won't load with blocked cookies

    App won't load with blocked cookies

    Hi, I had a weird behaviour on a customer's mobile device : cookies where blocked in safari and the whole app wouldn't load and triggered this error from redux-localstorage: DOM Exception 18: An attempt was made to break through the security policy of the user agent. Seems to be related to accessing localstorage, I don't know if it is by design or if it should fail without breaking the app.

    thanks, great work

    opened by jonathanasquier 7
  • Add EncodedLocalStorage

    Add EncodedLocalStorage

    Adds a local/session Storage that encodes the string before storing it and decodes it after getting it from the storage. Useful if you do not wish to store your state as plain text.

    opened by slybridges 7
  • .bablerc file preventing compilation with Babel 6

    .bablerc file preventing compilation with Babel 6

    I'm using Babel 6.24, and after installing this package, it throws:

    ERROR in ./~/redux-localstorage/lib/persistState.js
    Module build failed: ReferenceError:
    [BABEL] /home/emma/.../node_modules/redux-localstorage/lib/persistState.js:
    Using removed Babel 5 option: /home/emma/.../node_modules/redux-localstorage/.babelrc.stage -
    Check out the corresponding stage-x presets http://babeljs.io/docs/plugins/#presets
    

    After removing node_modules/redux-localstorage/.babelrc, everything works fine.

    opened by EmmaSimon 6
  • docs: fix typo

    docs: fix typo

    Minor grammar fix

    If you accept this PR, you might want to edit the repo's About description to match the change.

    Comparing omission of parenthetical modifiers:

    Before

    • Store enhancer that syncs (a subset) of your Redux store state to localstorage.
    • Store enhancer that syncs of your Redux store state to localstorage.

    After

    • Store enhancer that syncs (a subset of) your Redux store state to localStorage.
    • Store enhancer that syncs your Redux store state to localStorage.
    opened by jsejcksn 0
  • adapter for localForage is now unavailable from redux-localstorage

    adapter for localForage is now unavailable from redux-localstorage

    after I add @types/redux-localstorage, I could not use an adapter for a LocalForage type object.

    import adapter from 'redux-localstorage/lib/adapters/localForage'; is now throwing an error:

    TS2307: Cannot find module 'redux-localstorage/lib/adapters/localForage'.

    However, if I import it this way: import adapter from 'redux-localstorage/lib/adapters/AsyncStorage';, the terminal says Module not found: Can't resolve 'redux-localstorage/lib/adapters/AsyncStorage' though it exists

    can anyone help me with this issue? thanks!

    opened by kevademfidel 3
  • Ability to persist to local storage while the app is running (setInterval...)

    Ability to persist to local storage while the app is running (setInterval...)

    I'm using redux-localstorage in Electron, and in the case the app crashes or someone uses Ctrl-C from the app started from the terminal, the latest changes to redux during the session will not be persisted.

    What I would like to do is make sure it gets persisted from time to time, I'm not sure whether this is possible, it might be a more global issue with LocalStorage and how the application/browser exits, but I thought I'd ask if anyone has figured out a proper way of doing this.

    Thanks

    opened by lduros 0
  • Need to merge with updated defaultState

    Need to merge with updated defaultState

    If I add new things to a reducer, if that reducer was persisted to local storage it's not being merged with the new things being added to the reducer.

    Am I misunderstanding the merge property in the docs? Or is this not something this library is meant to do at all?

    opened by jrdn91 2
  • An example of how this works with the redux-devtools-extension?

    An example of how this works with the redux-devtools-extension?

    I've been trying to get this to work with composeWithDevTools from redux-devtools-extension like this

    import {createStore,applyMiddleware,compose} from 'redux';
    import { composeWithDevTools } from 'redux-devtools-extension';
    import thunk from 'redux-thunk';
    import persistState from 'redux-localstorage'
    
    const store = createStore(
      reducers,
      composeWithDevTools(
        applyMiddleware(thunk),
        persistState(),
      )
    )
    
    export default store
    

    However this breaks all the async requests in my app.

    Any ideas?

    opened by Huanzhang89 1
Owner
Elger Lambert
Elger Lambert
The state manager ☄️

☄️ effector The state manager Table of Contents Introduction Effector follows five basic principles: Installation Documentation Packages Articles Comm

effector ☄️ 4k Jan 5, 2023
[UNMAINTAINED] Reactive state and side effect management for React using a single stream of actions

Flexible state and side effect manager using RxJS for React. About Fluorine provides you with easy, reactive state and side effect management, accumul

Phil Pluckthun 285 Nov 20, 2022
Predictable state container for JavaScript apps

Redux is a predictable state container for JavaScript apps. (Not to be confused with a WordPress framework – Redux Framework) It helps you write appli

Redux 59.1k Jan 9, 2023
Official React bindings for Redux

React Redux Official React bindings for Redux. Performant and flexible. Installation Using Create React App The recommended way to start new apps with

Redux 22.5k Jan 3, 2023
redux higher order reducer + action to reduce actions under a single subscriber notification

redux-batched-actions Batching action creator and associated higher order reducer for redux that enables batching subscriber notifications for an arra

Tim Shelburne 1k Nov 21, 2022
Selector library for Redux

Reselect Simple “selector” library for Redux (and others) inspired by getters in NuclearJS, subscriptions in re-frame and this proposal from speedskat

Redux 18.8k Dec 27, 2022
Dead simple + opinionated toolkit for building redux/react applications

Simple opinionated toolkit for building applications using React, Redux, and Immutable.js You're early! There is sparse documentation, no tests, and i

shasta (archive) 517 Sep 29, 2022
COVID-19 Data: built with react and redux by using a public covid api

COVID-19 Data This is a React module capstone project. It is built with react and redux by using a public covid api. Screenshot Live Demo Live Link Vi

Tarikwa Tesfa 11 Jun 3, 2022
A performant, scalable and pluggable approach to instrumenting your React application.

react-i13n react-i13n provides a performant, scalable and pluggable approach to instrumenting your React application. Typically, you have to manually

Yahoo 369 Dec 25, 2022
React-crud-localstorage - A simple CRUD using React, TypeScript, Vite, Material UI and localStorage

React CRUD A simple CRUD using localStorage for a product's list. Live demo here

Fellipe Utaka 2 Feb 22, 2022
React-todo-localstorage - A simple Todo using React, TypeScript, Vite.js, Chakra UI and localStorage

React Todo A simple Todolist using localStorage. Live demo here. Table of Conten

Fellipe Utaka 3 Mar 8, 2022
store enhancer for https://github.com/reactjs/redux which allows batching subscribe notifications.

redux-batched-subscribe Store enhancer for redux which allows batching of subscribe notifications that occur as a result of dispatches. npm install --

Terry Appleby 500 Jan 3, 2023
🍙 Lightweight (600B minified + gzipped) React Hook to subscribe to a subset of your single app state.

?? useSubstate Lightweight (<600B minified + gzipped) React Hook to subscribe to a subset of your single app state. function Component() { const [su

Philipp Spiess 99 Apr 18, 2022
Official SweetAlert2 enhancer adding support for React elements as content

sweetalert2-react-content Official SweetAlert2 enhancer adding support for React elements as content. The following options can be React elements: tit

SweetAlert2 567 Dec 29, 2022
Redux bindings for React Router – keep your router state inside your Redux store

redux-router This project is experimental. For bindings for React Router 1.x see here In most cases, you don’t need any library to bridge Redux and Re

Andrew Clark 2.3k Dec 7, 2022
React Todo App allows you to create, edit, delete and view your todos. It is a simple todo app built with React, Linters and Localstorage.

React Todo App React Todo App allows you to create, edit, delete and view your todos. It is a simple todo app built with React, Linters and Localstora

Didier Peran Ganthier 8 Dec 20, 2022
Simple componentized localstorage implementation for Facebook's React.

React-LocalStorage Simply synchronize a component's state with localStorage, when available. This is an old-style Mixin, which means it's probably com

Samuel Reed 288 Jul 8, 2022
🔄 A collection of React hooks for reactively managing localStorage.

react-localstorage-hooks A collection of React hooks for reactively managing localStorage. Installation npm i react-localstorage-hooks Documentation

Akash Hamirwasia 8 Nov 29, 2022
📋 Task manager with react js you can add title add description , edit📝, complete✔, remove❌, localstorage, dark mode 🌜

?? Task manager with react js you can add title add description , edit??, complete✔, remove❌, localstorage, dark mode ??

ali turkaman 17 Sep 23, 2022
Reactive `localStorage` adapter for Angular applications 🎆

ngx-lss ngx-lss is an angular library that provides a reactive adapter for localStorage API Features ?? Provides Observable API ?? Allows watching cha

Viktor Chernodub 8 Dec 7, 2022