Convert Figma logo to code with AI

rt2zz logoredux-persist

persist and rehydrate a redux store

12,935
866
12,935
597

Top Related Projects

The official, opinionated, batteries-included toolset for efficient Redux development

8,475

The Redux Framework

27,458

Simple, scalable state management.

46,119

🐻 Bear necessities for state management in React

26,868

Actor-based state management & orchestration for complex app logic.

Quick Overview

Redux-persist is a library that enables automatic saving and rehydration of Redux stores. It allows developers to persist and rehydrate a Redux store, saving it to storage (like localStorage) and retrieving it when the app restarts, maintaining the state across sessions.

Pros

  • Seamless integration with Redux, requiring minimal setup
  • Supports multiple storage engines (localStorage, AsyncStorage, etc.)
  • Configurable persistence with blacklist/whitelist options
  • Provides migration capabilities for handling version changes

Cons

  • Can increase initial load time due to rehydration process
  • Potential for stale data if not managed properly
  • May complicate state management in larger applications
  • Limited built-in support for complex data structures

Code Examples

  1. Basic setup with Redux:
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'

const persistConfig = {
  key: 'root',
  storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = createStore(persistedReducer)
const persistor = persistStore(store)
  1. Using PersistGate in React:
import { PersistGate } from 'redux-persist/integration/react'

function App() {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <RootComponent />
      </PersistGate>
    </Provider>
  )
}
  1. Configuring persist with whitelist:
const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['auth', 'preferences']
}

Getting Started

  1. Install redux-persist:

    npm install redux-persist
    
  2. Set up your Redux store with redux-persist:

import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import rootReducer from './reducers'

const persistConfig = {
  key: 'root',
  storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = createStore(persistedReducer)
const persistor = persistStore(store)

export { store, persistor }
  1. Wrap your root component with PersistGate:
import { PersistGate } from 'redux-persist/integration/react'
import { store, persistor } from './store'

function App() {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <RootComponent />
      </PersistGate>
    </Provider>
  )
}

Competitor Comparisons

The official, opinionated, batteries-included toolset for efficient Redux development

Pros of Redux Toolkit

  • Provides a standardized way to write Redux logic, reducing boilerplate code
  • Includes utilities for common Redux use cases, such as creating slices and async thunks
  • Integrates well with TypeScript, offering improved type inference and safety

Cons of Redux Toolkit

  • Requires learning new APIs and patterns, which may be challenging for developers familiar with traditional Redux
  • May introduce additional dependencies and increase bundle size compared to a minimal Redux setup

Code Comparison

Redux Toolkit:

import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: state => state + 1,
  },
});

Redux Persist:

import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

const persistConfig = {
  key: 'root',
  storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);

Key Differences

  • Redux Toolkit focuses on simplifying Redux setup and reducing boilerplate, while Redux Persist specializes in persisting and rehydrating Redux store
  • Redux Toolkit provides a more opinionated approach to Redux development, whereas Redux Persist is a targeted solution for state persistence
  • Redux Toolkit is maintained by the Redux team, ensuring compatibility with core Redux, while Redux Persist is a community-driven project
8,475

The Redux Framework

Pros of Rematch

  • Simplifies Redux boilerplate with a more intuitive API
  • Built-in support for async actions and effects
  • Includes plugins for common Redux patterns (e.g., loading, immer)

Cons of Rematch

  • Smaller community and ecosystem compared to Redux Persist
  • May require learning a new mental model for state management
  • Less flexibility for complex persistence scenarios

Code Comparison

Redux Persist:

import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'

const persistConfig = {
  key: 'root',
  storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

Rematch:

import { init } from '@rematch/core'
import { getPersistor } from '@rematch/persist'

const store = init({
  models,
  plugins: [getPersistor()]
})

Key Differences

  • Redux Persist focuses solely on state persistence, while Rematch is a complete Redux framework with persistence capabilities
  • Rematch offers a more streamlined approach to Redux development, reducing boilerplate code
  • Redux Persist provides more granular control over persistence configuration
  • Rematch includes additional features like automatic code splitting and simplified async actions

Use Cases

  • Choose Redux Persist for projects requiring fine-grained control over state persistence in existing Redux applications
  • Opt for Rematch in new projects or when seeking to simplify Redux development with built-in persistence support
27,458

Simple, scalable state management.

Pros of MobX

  • Simpler and more intuitive API, requiring less boilerplate code
  • Automatic tracking of state changes and re-rendering of components
  • Better performance for complex state management scenarios

Cons of MobX

  • Less predictable state updates due to mutable state
  • Steeper learning curve for developers familiar with immutable state patterns
  • Potential for overuse of observables, leading to unnecessary re-renders

Code Comparison

MobX:

import { makeObservable, observable, action } from "mobx";

class Store {
  todos = [];

  constructor() {
    makeObservable(this, {
      todos: observable,
      addTodo: action
    });
  }

  addTodo(text) {
    this.todos.push({ text, completed: false });
  }
}

Redux-Persist:

import { createStore } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";

const persistConfig = {
  key: "root",
  storage,
};

const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer);
const persistor = persistStore(store);

While Redux-Persist focuses on persisting and rehydrating Redux state, MobX provides a more flexible state management solution. MobX allows for easier state updates and automatic tracking of changes, while Redux-Persist requires more setup but offers a more structured approach to state persistence.

46,119

🐻 Bear necessities for state management in React

Pros of Zustand

  • Simpler API and less boilerplate compared to Redux and Redux Persist
  • Built-in support for TypeScript without additional configuration
  • Smaller bundle size and better performance

Cons of Zustand

  • Less mature ecosystem and fewer middleware options
  • May require additional setup for complex persistence scenarios
  • Limited built-in support for time-travel debugging

Code Comparison

Redux Persist:

import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'

const persistConfig = {
  key: 'root',
  storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

Zustand:

import create from 'zustand'
import { persist } from 'zustand/middleware'

const useStore = create(persist(
  (set, get) => ({
    bears: 0,
    increasePopulation: () => set(state => ({ bears: state.bears + 1 })),
  }),
  { name: 'bear-storage' }
))

Both libraries offer state management solutions with persistence capabilities. Redux Persist is more established and integrates well with Redux ecosystems, while Zustand provides a simpler API and better performance for smaller to medium-sized applications. The choice between them depends on project requirements and complexity.

26,868

Actor-based state management & orchestration for complex app logic.

Pros of XState

  • More powerful state management with hierarchical and parallel states
  • Built-in visualization tools for complex state machines
  • TypeScript support out of the box

Cons of XState

  • Steeper learning curve due to state machine concepts
  • Potentially more verbose for simple state management needs
  • Less focused on persistence compared to Redux Persist

Code Comparison

XState:

import { createMachine, interpret } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: { on: { TOGGLE: 'active' } },
    active: { on: { TOGGLE: 'inactive' } }
  }
});

Redux Persist:

import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

const persistConfig = {
  key: 'root',
  storage,
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

XState offers a more structured approach to state management using state machines, which can be beneficial for complex applications. It provides built-in tools for visualization and supports TypeScript natively. However, it has a steeper learning curve and may be overkill for simpler state management needs.

Redux Persist, on the other hand, focuses specifically on persisting and rehydrating Redux stores. It's more straightforward to implement for basic state persistence but lacks the advanced state management features of XState.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Redux Persist

Persist and rehydrate a redux store.

build status npm version npm downloads

October 15th, 2021 - Move to TypeScript (Thanks @smellman)

As part of the work to upgrade the infrastructure used to build redux-persist, we're moving from Flow to TypeScript.

  • Move from Flow to TypeScript
  • Move from TravisCI to GitHub Actions (.github/workflows/ci.yml)
  • Version updates for some dependencies

September 22nd, 2021 - Under New Management

Redux Persist is a staple project for Redux developers, both on mobile and on the web. If you're here, it's likely because you need it now or have used it before and need to debug something, and like me have possibly struggled with making it work (especially with newer versions of things) and making it work with your code because the examples you'll find around the internet are inconsistent.

I (@ckalika) spoke with @rt2zz about taking over maintenance of the project, and we agreed to give it a shot and see how we go. My priorities are as follows:

  1. Go through and triage the existing issues

    • Separate them into bugs, feature requests, basic questions/requests for code samples, and issues that are either not project-specific or don't fall within the remit of the project (specific definitions and criteria will be posted in the future)
    • Determine the severity/urgency of each bug or feature request
    • Guestimate the size of them
    • Determine which are actionable immediately or in the short term
    • Establish some semblance of test criteria for each
  2. Upgrade dependencies (where possible) so that we've got something building with modern versions

    • Note: Right now, it's about modernising the project infrastructure and build process without making breaking API changes
  3. Go through the existing pull requests

    • Merge the ones that deal with documentation, code samples, etc.
    • Review and merge the ones that deal with open issues
    • Review and merge the ones that will require breaking changes and consult authors about redux-persist@v7 (feature set and requirements to be defined)
  4. Update the documentation

    • Split it out for both web and mobile
    • Providing code samples and test coverage for how to use the library
    • Provide or link to working examples that integrate with additional libraries (e.g. RTK Query).
  5. Improve testing and automation

    • Move to GitHub Actions
    • Move from Ava to Jest

There's a lot to do here, so I'll ask your patience and understanding as I work through it. If you have ideas for how to improve the library, the documentation, or the community, I'd love to hear them, and if you're submitting pull requests (or have submitted some previously), please reach out and help me understand what you're aiming to do with it.

I'll try to get some discussions up to pull together ideas, so we can properly work out what the next version is likely to look like.

v6 upgrade

Web: no breaking changes React Native: Users must now explicitly pass their storage engine in. e.g.

import AsyncStorage from '@react-native-async-storage/async-storage';

const persistConfig = {
  //...
  storage: AsyncStorage
}

Quickstart

npm install redux-persist

Usage Examples:

  1. Basic Usage
  2. Nested Persists
  3. Hot Module Replacement
  4. Code Splitting [coming soon]

Basic Usage

Basic usage involves adding persistReducer and persistStore to your setup. IMPORTANT Every app needs to decide how many levels of state they want to "merge". The default is 1 level. Please read through the state reconciler docs for more information.

// configureStore.js

import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web

import rootReducer from './reducers'

const persistConfig = {
  key: 'root',
  storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

export default () => {
  let store = createStore(persistedReducer)
  let persistor = persistStore(store)
  return { store, persistor }
}

If you are using react, wrap your root component with PersistGate. This delays the rendering of your app's UI until your persisted state has been retrieved and saved to redux. NOTE the PersistGate loading prop can be null, or any react instance, e.g. loading={<Loading />}

import { PersistGate } from 'redux-persist/integration/react'

// ... normal setup, create store and persistor, import components etc.

const App = () => {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <RootComponent />
      </PersistGate>
    </Provider>
  );
};

API

Full API

persistReducer(config, reducer)

  • arguments
    • config object
      • required config: key, storage
      • notable other config: whitelist, blacklist, version, stateReconciler, debug
    • reducer function
      • any reducer will work, typically this would be the top level reducer returned by combineReducers
  • returns an enhanced reducer

persistStore(store, [config, callback])

  • arguments
    • store redux store The store to be persisted.
    • config object (typically null)
      • If you want to avoid that the persistence starts immediately after calling persistStore, set the option manualPersist. Example: { manualPersist: true } Persistence can then be started at any point with persistor.persist(). You usually want to do this if your storage is not ready when the persistStore call is made.
    • callback function will be called after rehydration is finished.
  • returns persistor object

persistor object

  • the persistor object is returned by persistStore with the following methods:
    • .purge()
      • purges state from disk and returns a promise
    • .flush()
      • immediately writes all pending state to disk and returns a promise
    • .pause()
      • pauses persistence
    • .persist()
      • resumes persistence

State Reconciler

State reconcilers define how incoming state is merged in with initial state. It is critical to choose the right state reconciler for your state. There are three options that ship out of the box, let's look at how each operates:

  1. hardSet (import hardSet from 'redux-persist/lib/stateReconciler/hardSet') This will hard set incoming state. This can be desirable in some cases where persistReducer is nested deeper in your reducer tree, or if you do not rely on initialState in your reducer.
    • incoming state: { foo: incomingFoo }
    • initial state: { foo: initialFoo, bar: initialBar }
    • reconciled state: { foo: incomingFoo } // note bar has been dropped
  2. autoMergeLevel1 (default) This will auto merge one level deep. Auto merge means if the some piece of substate was modified by your reducer during the REHYDRATE action, it will skip this piece of state. Level 1 means it will shallow merge 1 level deep.
    • incoming state: { foo: incomingFoo }
    • initial state: { foo: initialFoo, bar: initialBar }
    • reconciled state: { foo: incomingFoo, bar: initialBar } // note incomingFoo overwrites initialFoo
  3. autoMergeLevel2 (import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2') This acts just like autoMergeLevel1, except it shallow merges two levels
    • incoming state: { foo: incomingFoo }
    • initial state: { foo: initialFoo, bar: initialBar }
    • reconciled state: { foo: mergedFoo, bar: initialBar } // note: initialFoo and incomingFoo are shallow merged

Example

import hardSet from 'redux-persist/lib/stateReconciler/hardSet'

const persistConfig = {
  key: 'root',
  storage,
  stateReconciler: hardSet,
}

React Integration

Redux persist ships with react integration as a convenience. The PersistGate component is the recommended way to delay rendering until persistence is complete. It works in one of two modes:

  1. loading prop: The provided loading value will be rendered until persistence is complete at which point children will be rendered.
  2. function children: The function will be invoked with a single bootstrapped argument. When bootstrapped is true, persistence is complete and it is safe to render the full app. This can be useful for adding transition animations.

Blacklist & Whitelist

By Example:

// BLACKLIST
const persistConfig = {
  key: 'root',
  storage: storage,
  blacklist: ['navigation'] // navigation will not be persisted
};

// WHITELIST
const persistConfig = {
  key: 'root',
  storage: storage,
  whitelist: ['navigation'] // only navigation will be persisted
};

Nested Persists

Nested persist can be useful for including different storage adapters, code splitting, or deep filtering. For example while blacklist and whitelist only work one level deep, but we can use a nested persist to blacklist a deeper value:

import { combineReducers } from 'redux'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'

import { authReducer, otherReducer } from './reducers'

const rootPersistConfig = {
  key: 'root',
  storage: storage,
  blacklist: ['auth']
}

const authPersistConfig = {
  key: 'auth',
  storage: storage,
  blacklist: ['somethingTemporary']
}

const rootReducer = combineReducers({
  auth: persistReducer(authPersistConfig, authReducer),
  other: otherReducer,
})

export default persistReducer(rootPersistConfig, rootReducer)

Migrations

persistReducer has a general purpose "migrate" config which will be called after getting stored state but before actually reconciling with the reducer. It can be any function which takes state as an argument and returns a promise to return a new state object.

Redux Persist ships with createMigrate, which helps create a synchronous migration for moving from any version of stored state to the current state version. [Additional information]

Transforms

Transforms allow you to customize the state object that gets persisted and rehydrated.

There are several libraries that tackle some common implementations for transforms.

  • immutable - support immutable reducers
  • seamless-immutable - support seamless-immutable reducers
  • compress - compress your serialized state with lz-string
  • encrypt - encrypt your serialized state with AES
  • filter - store or load a subset of your state
  • filter-immutable - store or load a subset of your state with support for immutablejs
  • expire - expire a specific subset of your state based on a property
  • expire-reducer - more flexible alternative to expire transformer above with more options

When the state object gets persisted, it first gets serialized with JSON.stringify(). If parts of your state object are not mappable to JSON objects, the serialization process may transform these parts of your state in unexpected ways. For example, the javascript Set type does not exist in JSON. When you try to serialize a Set via JSON.stringify(), it gets converted to an empty object. Probably not what you want.

Below is a Transform that successfully persists a Set property, which simply converts it to an array and back. In this way, the Set gets converted to an Array, which is a recognized data structure in JSON. When pulled out of the persisted store, the array gets converted back to a Set before being saved to the redux store.

import { createTransform } from 'redux-persist';

const SetTransform = createTransform(
  // transform state on its way to being serialized and persisted.
  (inboundState, key) => {
    // convert mySet to an Array.
    return { ...inboundState, mySet: [...inboundState.mySet] };
  },
  // transform state being rehydrated
  (outboundState, key) => {
    // convert mySet back to a Set.
    return { ...outboundState, mySet: new Set(outboundState.mySet) };
  },
  // define which reducers this transform gets called for.
  { whitelist: ['someReducer'] }
);

export default SetTransform;

The createTransform function takes three parameters.

  1. An "inbound" function that gets called right before state is persisted (optional).
  2. An "outbound" function that gets called right before state is rehydrated (optional).
  3. A config object that determines which keys in your state will be transformed (by default no keys are transformed).

In order to take effect transforms need to be added to a PersistReducer’s config object.

import storage from 'redux-persist/lib/storage';
import { SetTransform } from './transforms';

const persistConfig = {
  key: 'root',
  storage: storage,
  transforms: [SetTransform]
};

Storage Engines

Community & Contributing

I will be updating this section shortly. If you have a pull request that you've got outstanding, please reach out and I will try to review it and get it integrated. As we've shifted to TypeScript, that may necessitate some changes, but I'm happy to help in that regard, wherever I can.

NPM DownloadsLast 30 Days