Chiamaka Umeh A frontend developer with a passion for designing highly-responsive user interfaces for JavaScript-based web and mobile apps using React and React Native.

How to use Redux Persist in React Native

5 min read 1612

Redux Persist React Native

Sometimes, a developer needs to persist information locally for a user. For example, let’s say you built an ecommerce platform where users can add items to their cart without signing in. You need to implement a way for the app to remember what items the user added to the cart. It’s a bad experience for the user to close the app for a while, only to find that the items they added to the cart are no longer there.

Another scenario would be when after a user signed into your app, they closed the app for a minute, only to be taken to the login page to log in again.

In this article, we’ll learn how to use Redux Persist to persist the Redux store. First, we’ll build a small to-do application and set up its state management with Redux. Then, we’ll set up the Redux Persist library for data preservation in scenarios where the app is closed. Let’s get started!

Redux Persist and React Native

In React Native applications, data can be persisted locally using AsyncStorageAsyncStorage is an asynchronous, persistent, key-value storage system that is global to the entire app.

Redux Persist is a tool used to seamlessly save the application’s Redux state object to AsyncStorage. On app launch, Redux Persist retrieves this persisted state and saves it back to Redux.

Normally, Redux sets the initial state of your app upon launch. Soon after, Redux Persist fetches your persisted state, overriding any initial state in a process the Redux Persist team calls rehydration.

Redux is an open source JavaScript library for managing and centralizing application state. It maintains the state of an entire application in a single immutable state object, which can’t be changed directly. When something changes, a new object is created using actions and reducers.

Setting up the project

In this tutorial, we’ll demonstrate implementing Redux Persist on the Expo snack platform.

Let’s create a folder called screens. Inside it, create a file called Todo.js, which will contain the input field to add to-dos and also display the to-dos added:

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function BooksListApp() {
  return (
    <View style={styles.container}>
        <Text style={styles.paragraph}>React Native ToDo App with Redux Persist </Text>
          <Text style={styles.title}> Add ToDo Here</Text>
          <TextInput
            style={styles.input}
            mode="outlined"
            label="Task"
            value={task}
             onChangeText={task => setTask(task)}
          />
          <Button title='Add' color="#841584"  />
      <FlatList
        data={todos}
        keyExtractor={(item) => item.id}
        renderItem={({item, index}) => {
          return (
              <Text style={styles.list}>{item.task}</Text>
          );
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 10,
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  title: {
    margin: 10,
    fontSize: 16,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  list: {
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  input: {
    height: 40,
    margin: 12,
    borderWidth: 1,
    padding: 10,
  },
});

Setting up Redux

To set up Redux, first install it with the following code:

npm install redux

Creating action creators

With Redux, the state of the whole application is managed by one JavaScript object. To make changes to that object, we can dispatch actions.

Let’s create another folder called redux for state management. Inside it, let’s create an action.js file to set up the action types and action creators:

export const ADD_TODO = "ADD_TODO";

let todoId = 0;

export const addTodo = task => ({
  type: ADD_TODO,
  payload: {
    id: ++todoId,
    task
  }
});

The action type ADD_TODO is self-explanatory. We call it when we need to update the state by adding a new todo.

Creating the reducer

When an action has been dispatched, a reducer updates the state by making the changes to the state object. The reducer is a pure function that takes two inputs, the state and action, and must return the default state.

Let’s create another file in the redux folder and import the action type we created earlier. We’ll also create an initial app state:

import { ADD_TODO } from "./action";

const initialState = {
  todos: []
};

const todoReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO: {
      const { id, task } = action.payload
      return {
        ...state,
        todos: [ ...state.todos, { id, task }]
      };
    }
    default:
      return state;
  }
}

export default todoReducer;

In the code above, we defined a reducer function, todoReducer, which takes initialState as the default value for the first argument and action as the second argument.

Setting up the Redux store

The Redux store holds the state at the application level so that it can be accessed from any component. The Redux store is an object that brings actions and reducers together.

We’ll use the createStore method from Redux to configure a Redux store. It takes the reducer as an argument.



Create a store.js file inside the redux folder and initialize the Redux store as follows:

import { createStore } from "redux";
import todoReducer from './reducers';

export default createStore(todoReducer);

Next, we’ll make the Redux store globally available by wrapping the entire app in a higher order component called Provider and passing the store to it.

In the App.js file, import Provider from React Redux and wrap the app in it:

import store from './redux/store';
import { Provider } from 'react-redux';

const App = () => {
  return (
    <Provider store={store}>
      <MyTodo/>
    </Provider>
  );
}

export default App;

In the code above, we have made a connection between the React components and the Redux store and state. Therefore, any component in this application can access the app’s state at any point.

Dispatching an action

With Redux set up, let’s write a function that will dispatch an action when a user adds a to-do item. In Todo.js, add the code below:

import { useSelector, useDispatch } from 'react-redux';
import { addTodo } from './redux/action';

const [task, setTask] = React.useState('');
  const  todoList  = useSelector(state => state.todos);
  const dispatch = useDispatch();


  const handleAddTodo = () => {
    dispatch(addTodo(task))
    setTask('')
  }

We imported the useSelector Hook from React Redux for getting the state in this component and useDispatch for dispatching an action. We then call the function when the Add button is clicked:

<Button title='Add' color="#841584" onPress={handleAddTodo} />

Now, when we add an item in the input field and click the button, the addTodo action creator is called, which, in turn, dispatches the ADD_TODO action to the reducer so that the item is added and the app’s state is updated.

At this point, you’ll notice that the reducer is working, and we have our to-do app up and running. But, you’ll notice that when we close the application for a while and open it again, all the items we added are gone, indicating that the Redux state has been reset to initial state.

We can persist the items we added using Redux Persist. Therefore, even when we close the application and open it again, we don’t lose our to-do items.

Integrating Redux persist

Let’s install the Redux Persist package and the React Native AsyncStorage package with the following command:

npm i redux-persist @react-native-async-storage/async-storage

We’ll import persistStore, persistReducer from Redux Persist and add the following configuration:

import AsyncStorage from '@react-native-async-storage/async-storage';
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' 

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

To create the configuration for React Persist, you pass the key and the storage engine, which are required. There are other optional configurations like whitelist, blacklist, version, stateReconciler, and debug. If you don’t want to persist a part of your state, you could put it in the blacklist. Alternately, the whitelist defines the parts of the state you wish to persist.

Now, we’ll call the persistReducer, passing in the config object and our todoReducer.
We also export the persistor, which is an object that is returned by persistStore that wraps the original store:

const persistedReducer = persistReducer(persistConfig, todoReducer)

export const store = createStore(persistedReducer)
export const persistor = persistStore(store)

At the end of the configuration, our store.js will look like the following code:

import AsyncStorage from '@react-native-async-storage/async-storage';
import { persistStore, persistReducer } from 'redux-persist'

import todoReducer from './reducers';
import { createStore } from 'redux'
import storage from 'redux-persist/lib/storage' 

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

const persistedReducer = persistReducer(persistConfig, todoReducer)

export const store = createStore(persistedReducer)
export const persistor = persistStore(store)

Then, we update App.js by importing PersistGate and wrapping the app in it:

import * as React from 'react';
import { Text } from 'react-native';
import { PersistGate } from 'redux-persist/integration/react'
import MyTodo from './Todo';


import {store, persistor} from './redux/store';
import { Provider } from 'react-redux';

const App = () => {
  return (
    <Provider store={store}>
    <PersistGate loading={<Text>Loading...</Text>} persistor={persistor}>
      <MyTodo/>
    </PersistGate>
    </Provider>
  );
}

export default App;

Redux Persist persists the Redux store for us in the AsyncStorage. Even when we leave the app and come back later, it fetches the data from AsyncStorage and initializes the Redux store with it.


More great articles from LogRocket:


Conclusion

In this article, we reviewed what Redux Persist is and how to set it up in a React Native application. Redux Persist is helpful when you need to persist login sessions and other information without a database. It greatly improves the user experience by allowing your users to exit and return to your application without losing their data. I hope you enjoyed this article, and be sure to leave a comment if you have any questions. Happy coding!

LogRocket: Instantly recreate issues in your React Native apps.

LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.

LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.

Start proactively monitoring your React Native apps — .

Chiamaka Umeh A frontend developer with a passion for designing highly-responsive user interfaces for JavaScript-based web and mobile apps using React and React Native.

Leave a Reply