Redux-persist: Need an example

Created on 16 May 2016  路  29Comments  路  Source: rt2zz/redux-persist

I'm completely new to redux. I want to persist the state can anyone please give some good example how to persist the state and also per reducer. I saw some examples in closed issues but it was not clear for me. Thank you.

Most helpful comment

Yes, the documentation is too general and not accessible to mere mortals

All 29 comments

@damamsrinivas in most cases the "simplest usage" will work.

Can you explain more about what you are looking for? are you building a web app?

I am also a bit lost, I have to admit. I have implemented as pointed out the "simplest usage". But then what? My use case: I want to re-populate a form with values a user may have filled out. I use redux-form as form library where an example is given to set initial values. Well, it might be very specific to redux-form, but I try to retrieve persisted value from the store without success so far.

I have this but it doesnt work at all

import { createStore, applyMiddleware, compose} from 'redux';
import { connect, Provider } from 'react-redux';

import reducers from './reducers';

// create global store...
import thunkMiddleware from 'redux-thunk';
import {persistStore, autoRehydrate} from 'redux-persist'

const middleware = [thunkMiddleware, autoRehydrate() ];
let store = compose(
  applyMiddleware(...middleware)
)(createStore)(reducers);
persistStore(store, {storage: AsyncStorage})

@Anthonyzou autoRehydrate is a store enhancer, not a middleware, so your code should look more like:

const middleware = [thunkMiddleware];
let store = compose(
  applyMiddleware(...middleware),
  autoRehydrate()
)(createStore)(reducers);
persistStore(store, {storage: AsyncStorage})

@rt2zz Thanks! I'm pretty new to redux. That makes more sense now.

@rt2zz I tried your suggestion. Its messing up react-router, i.e, if I click any other link it will redirect to first URL page has been loaded.

E.g.: Loaded react app at '/' then clicking any other page then the page redirects to same page. If in new browser session, any other page is loaded, other than '/', page then that page gets redirected to.

Fixed the problem. I am using react-router-redux npm module and so I had to set routing key as blacklist like below:

persistStore(store, {blacklist: ['routing']});

The key was storing URL slug and so was creating problem.

馃憤 If you have a chance to PR adding in a note regarding this in the README that would be awesome 馃帀

You can find fully working example here.

Yes, the documentation is too general and not accessible to mere mortals

I am all for making the docs better, what about it do you feel is to general? It sounds like it might need more concrete examples?

Personally - when I first started using this, I was still new to the overall react/react-native/redux ecosystem. I knew the general terms but very specific buzzwords were being thrown around like enhancer vs middleware etc

I'm slowly starting to understand more as I spend more time looking into things - but that's coming from a mixture of googling / stack overflow etc

From my experience: whenever someone draws a diagram with arrows labeling information flow / actions (not in the redux sense of the word). The most helpful Redux explanations for me came from short youtube videos that explained the information flow and showed specific snippets of code.

Hi,
I've setup the configurations as mentioned in Basic Usage, but there's a thing that makes me confused.
I used to get the initialState from localStorage with this example code:
const initialState = { cart: localStorage.getValue("key") ? localStorage.getValue("key") : [] };
so, what should be changed now?
this is in one my redux modules which I separate from configs

Im using quite custom setup as I wanted to get the initial state synchronously

persistStore(store, {
    storage: {
        getItem: (key, cb) => cb(null, localStorage.getItem(key)),
        setItem: (key, item) => localStorage.setItem(key, item),
        getAllKeys: cb => cb(null, Object.keys(localStorage)),
    },
})

and just handle the REHYDRATE action in ur reducers

Keep in mind that this example uses window.localStorage which is not guarded and may throw in some cases, so its best to use some abstraction which is guarded. Ive written such (and planning to make a package out of it, but hadnt yet have time for this), so you can use it:

const testKey = '__test_storage_support__'

const isSupported = (storage => {
    try {
        storage.setItem(testKey, true)
        storage.getItem(testKey)
        storage.removeItem(testKey)
        return true
    } catch (err) {
        return false
    }
})(window.localStorage)

const memoryStorage = Object.defineProperties(
    {},
    {
        getItem: {
            value: key => memoryStorage[key] || null,
        },
        setItem: {
            value: (key, value) => {
                try {
                    memoryStorage[key] = String(value)
                } catch (err) {} // eslint-disable-line no-empty
            },
        },
        removeItem: {
            value: key => {
                delete memoryStorage[key]
            },
        },
        clear: {
            value: () => {
                Object.keys(memoryStorage).forEach(memoryStorage.removeItem)
            },
        },
        key: {
            value: index => Object.keys(memoryStorage)[index] || null,
        },
        length: {
            get() {
                return Object.keys(memoryStorage).length
            },
        },
    },
)

export default (isSupported ? window.localStorage : memoryStorage)

@Andarist that memory fallback is great and some variant of that should definitely be the default storage we ship with. I think most non-trivial applications have implemented something similar so would be great to standardize.

what do u have in mind exactly - what variant should we incorporate? I think for the redux-persist fallbacking to noops is just fine

Sorry, but I am not grasping the basics here, its far too verbose, and there are not really any plain and simple examples, they are all convoluted, or assume far too much.

I'm trying to set this up with my react Redux app to save a simple 'loggedIn' state flag, so I can persist it on page refreshes. However, having spent three hours trying to actually apply the examples in this documentation, I still haven't got anything to work at all LocalStorage is always empty, any getItem returns null.

It would be really nice for a change that Redux complimentary modules such as this do not follow with the tradition of overly elaborate and poorly written documentation, from which Redux itself badly suffers

I've been writing React / Redux apps for quite a while, now wishing to use storage like this, I simply cannot grasp the basis. Could some kind soul write some documentation or examples for us mere mortals??

Taking that the persistedState is an initialState with all flags: my store looks thus: (ES6)

const store = createStore(
       ApplicationReducer,
       persistedState,
       compose(
            applyMiddleware(thunk),
            autoRehydrate(),
            window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
       )
    );

persistStore(store);

export const configureStore =  () => store

This seems to have absolutely no effect. Looking in Chrome-dev tools at the LocalStorage its blank, Even adding things to the Redux state has no effect on LocalStorage

So have I totally misinterpreted this library, or is there something glaringly obvious I'm missing. Plain English answers and examples are appreciated?

I think a simple setup can be summarised in a few steps:

  1. Install redux-persist in you project.
  2. Import persistStore and autoRehydrate form redux-persist.
  3. Add autoRehydrate to your store.
  4. Pass your store to persistStore
  5. Listen to the persist/REHYDRATE action on your reducer and populate state accordingly.

Here's a simple example I've put together based on the steps above:

/* store.js */

// Store of the application. Reducer will go through here and update app.
import { applyMiddleware, createStore, compose } from "redux"

import logger from "redux-logger"
import ReduxThunk from 'redux-thunk'
import promise from "redux-promise-middleware"
import { persistStore, autoRehydrate } from 'redux-persist'
import reducer from "./Reducers/"

const middleware = applyMiddleware(promise(), ReduxThunk, logger)

// The store now uses redux persist.
const store = createStore(
  reducer,
  { /* Initial state if you wish. */  },
  compose(middleware, autoRehydrate())
)

// This line makes store persistent.
persistStore(store)

export default store

Listening to rehydration action in a reducer.

/* reducer.js */

export default function reducer( state=initialState, action ) {
  switch (action.type) {
    case "persist/REHYDRATE": {
      return { ...state, ...action.payload }
    }
    default: {
      return state;
    }
  } // Switch
}

If you still find it difficult to understand, Clark Sanford wrote a tutorial on Medium on how to set this module up!

@csalmeida Should we write the rehydration action in every reducer or is there a single place?

@aswinalapati Actually, it should work without the reducer, you can check if it's working by setting a redux logger as middleware up, it will show if the persist/REHYDRATE was dispatched. You could also run window.localStorage in your console and it will show if you have any data stored there.

You could setup the reducer as a way to control what parts of the store you want to be passed into localStorage as the default behaviour is to pull the whole store. You can set it up just once and reuse it as needed of course. :)

Is it possible to get a example on redux-persist v5? Cant get it working correct

@Zwirc This is how I setup my store with v5, hope it helps:

import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import {
  persistStore,
  persistCombineReducers,
} from 'redux-persist';
import { AsyncStorage } from 'react-native';
import { composeWithDevTools } from 'redux-devtools-extension';
import todos from './reducers/todos';

const config = {
  key: 'root',
  storage: AsyncStorage,
};

const reducers = persistCombineReducers(config, {
  todos,
});

export const configureStore = () => {
  const store = createStore(
    reducers,
    compose(
      applyMiddleware(thunk),
      composeWithDevTools()
    )
  );
  const persistor = persistStore(store);

  return { persistor, store };
};

Then in your App.js use store as sample code in PersistGate

I'm following the v5 example closely combined with the Redux Todo Example and I've noticed that as soon as I add the visibilityFilter reducer in and complete a todo; I get this error on next load of the app. Any thoughts?

Unhandled Promise rejection: TypeError: In this environment the sources for assign MUST be an object.This error is a performance optimization and not spec compliant.

@joelash i'm facing the same problem too

any solution to this ?

Hello guys,
I have this example repo with react native for redux-persist v5. Please have a look on it give me your review if anything is missing or some other need to be add.

PS: handling persisted data is new for me so in that area I need more assistance.

Thanks

@ranggarifqi I have not found a solution, I was early in my needs to I've just ignored this for now. By chance have you seen redux-storage? It seems to solve many of the same issues.

@Shhzdmrz thanks for the example repo; I'll take a look this week when I get a chance

Right now redux-persist assumes all sub states are a plain object. The todo example has visibilityFilter as a string hence the issue. I would like to update redux-persist to remove this assumption.

fix for this is available in [email protected], available via npm i redux-persist@next

For anyone looking to implement v5 of redux persist, here's a detailed write-up on medium.
https://medium.com/@thenewt15/the-definitive-guide-to-redux-persist-84738167975

Was this page helpful?
0 / 5 - 0 ratings