Which is Better State Persistence Libraries for React Applications?
redux-persist vs react-hook-form-persist
1 Year
redux-persistreact-hook-form-persistSimilar Packages:
What's State Persistence Libraries for React Applications?

State persistence libraries are essential tools in React applications that help maintain and restore the state of forms or application data across sessions. They allow developers to save the state of their application in local storage or other storage mechanisms, ensuring that user inputs or application states are not lost when the page is refreshed or the application is reopened. This enhances user experience by providing continuity and reducing frustration caused by data loss.

NPM Package Downloads Trend
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
redux-persist992,98312,951-5965 years agoMIT
react-hook-form-persist15,38716712.4 kB17-MIT
Feature Comparison: redux-persist vs react-hook-form-persist

Integration with State Management

  • redux-persist: redux-persist integrates directly with Redux, allowing you to persist the entire Redux state tree. This means that any part of your application that relies on Redux can benefit from state persistence, making it suitable for applications with complex state management needs.
  • react-hook-form-persist: react-hook-form-persist is specifically designed to work with react-hook-form, a popular library for managing form state in React. It allows for easy integration, enabling developers to persist form data without needing to manage additional state or boilerplate code. This makes it an excellent choice for applications focused on forms.

Data Persistence Scope

  • redux-persist: redux-persist allows for broader data persistence across the entire Redux state. This means that not only form data but also other application states, such as user preferences, authentication tokens, and UI states, can be persisted, providing a more comprehensive solution for state management.
  • react-hook-form-persist: The scope of data persistence in react-hook-form-persist is limited to form data. It is designed to handle the specific needs of forms, such as input values and validation states, ensuring that users do not lose their progress when navigating away from the form or refreshing the page.

Ease of Use

  • redux-persist: redux-persist requires a bit more setup and configuration compared to react-hook-form-persist, as it involves integrating with Redux and defining which parts of the state should be persisted. However, once set up, it provides a powerful solution for managing global state persistence.
  • react-hook-form-persist: react-hook-form-persist is straightforward to set up and use, especially for developers already familiar with react-hook-form. It requires minimal configuration and allows for quick implementation of form state persistence, making it accessible for developers of all skill levels.

Performance Considerations

  • redux-persist: redux-persist can introduce performance overhead if not configured correctly, especially if large portions of the Redux state are persisted. Developers need to carefully choose which parts of the state to persist and may need to implement strategies like selective persistence to optimize performance.
  • react-hook-form-persist: react-hook-form-persist is optimized for form handling, ensuring that performance is not significantly impacted when persisting form state. It focuses on only the necessary data, leading to efficient storage and retrieval processes without overwhelming the local storage with unnecessary data.

Use Cases

  • redux-persist: Best suited for applications that utilize Redux for state management and require persistence of various application states, such as e-commerce platforms, dashboards, or any application where maintaining user preferences and session data is essential.
  • react-hook-form-persist: Ideal for applications with complex forms, such as registration forms, surveys, or any scenario where user input is critical and should be preserved across sessions. It is particularly useful in single-page applications (SPAs) where users may navigate away from forms frequently.
How to Choose: redux-persist vs react-hook-form-persist
  • redux-persist: Choose redux-persist if your application uses Redux for state management and you want to persist the entire Redux state tree. This is beneficial for applications that require a global state management solution and need to maintain user preferences, authentication states, or other global data across sessions.
  • react-hook-form-persist: Choose react-hook-form-persist if your primary focus is on managing form state in React applications. It integrates seamlessly with react-hook-form, allowing you to persist form data efficiently and restore it when needed, making it ideal for applications with complex forms that require validation and dynamic inputs.
README for redux-persist

Redux Persist

Persist and rehydrate a redux store.

build status npm version npm downloads #redux-persist on Discord

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 peristor.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 of the 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

Chat Room

#redux-persist on Discord #redux-persist channel in the Reactiflux Discord

Blog articles from the community