React Native + Redux: Why You Should Be Using Redux-Persist (6.x) To Save Your Redux Store From Disappearing

By: Jeff Lewis (Updated: 10/30/2019)

Notes:

This guide assumes you know React Native and Redux .

and . Github Repo: https://github.com/jefelewis/redux-persist-demo

1. What Is Redux-Persist?

Say you’re using your app and at some point, you decide to clear out all of your apps because you have too many open. You then click on your app icon and it launches, but you’re now prompted with a Login screen.

You didn’t press log out, so why did you get logged out? It’s because the state isn’t being saved when the app is closed. Redux-Persist saves the Redux Store when the app is closed or refreshed in the iPhone simulator.

2. How Do We Use Redux Persist?

A. NPM Dependencies Explained:

B. Install Dependencies:

npm install redux react-redux redux-persist @react-native-community/async-storage --save

C. Install Dependencies (Development):

npm install redux-logger --save-dev

3. Example App + Code

Github Repo: https://github.com/jefelewis/redux-persist-test

A. App Overview

The App will have two Reducers: authReducer and counterReducer. In our store.js file, we can Blacklist or Whitelist reducers to persist data from only specific reducers. In our case, we are only going to persist the data from the authReducer.

To test that Redux Persist is working in our app, we can click the login button (Status should change to true) and increase the counter to 5. When we refresh our app, the Logged In status should remain true (Whitelisted) and the counter should be reset to 0 (Blacklisted).

B. App Screenshot

App Screenshot

C. App File Structure

This example will be using 8 files:

App.js (React Native App) Counter.js (Counter Screen) store.js (Redux Store) index.js (Redux Root Reducer) authActions.js (Authentication Actions) counterActions.js (Counter Actions) authReducer.js (Redux Auth Reducer) counterReducer.js (Redux Counter Reducer)

D. App Files

App.js

// Imports: Dependencies

import React from 'react';

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

import { Provider } from 'react-redux'; // Imports: Screens

import Counter from './screens/Counter'; // Imports: Redux Persist Persister

import { store, persistor } from './store/store'; // React Native: App

export default function App() {

return (

// Redux: Global Store

<Provider store={store}>

<PersistGate loading={null} persistor={persistor}>

<Counter />

</PersistGate>

</Provider>

);

};

Counter.js

// Imports: Dependencies

import React, { Component } from 'react';

import { Button, Dimensions, SafeAreaView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';

import { connect } from 'react-redux'; // Screen Dimensions

const { height, width } = Dimensions.get('window'); // Screen: Counter

class Counter extends React.Component {

render() {

return (

<SafeAreaView style={styles.container}>

<View style={styles.loggedInContainer}>

<Text style={styles.loggedInText}>Logged In: </Text>

<Text style={styles.loggedInText}>{`${this.props.loggedIn}`}</Text> <Button

title="Login"

onPress={this.props.loggedIn === false ? () => this.props.reduxLogin(true) : () => this.props.reduxLogin(false)}

style={styles.loginButton}

/>

</View> <Text style={styles.counterTitle}>Counter</Text> <View style={styles.counterContainer}>

<TouchableOpacity onPress={() => this.props.reduxIncreaseCounter()}>

<Text style={styles.buttonText}>+</Text>

</TouchableOpacity> <Text style={styles.counterText}>{this.props.counter}</Text <TouchableOpacity onPress={() => this.props.reduxDecreaseCounter()}>

<Text style={styles.buttonText}>-</Text>

</TouchableOpacity>

</View>

</SafeAreaView>

)

}

} // Styles

const styles = StyleSheet.create({

container: {

flex: 1,

justifyContent: 'center',

alignItems: 'center',

},

loggedInContainer: {

display: 'flex',

flexDirection: 'column',

justifyContent: 'center',

alignItems: 'center',

marginBottom: 40,

},

loginButton: {

marginTop: 20,

paddingTop: 20,

},

counterContainer: {

display: 'flex',

flexDirection: 'row',

justifyContent: 'center',

alignItems: 'center',

},

loggedInText: {

fontFamily: 'System',

fontSize: 17,

fontWeight: '400',

color: '#000',

},

counterTitle: {

fontFamily: 'System',

fontSize: 32,

fontWeight: '700',

color: '#000',

},

counterText: {

fontFamily: 'System',

fontSize: 36,

fontWeight: '400',

color: '#000',

},

buttonText: {

fontFamily: 'System',

fontSize: 50,

fontWeight: '300',

color: '#007AFF',

marginLeft: 40,

marginRight: 40,

},

}); // Map State To Props (Redux Store Passes State To Component)

const mapStateToProps = (state) => {

// Redux Store --> Component

return {

counter: state.counterReducer.counter,

loggedIn: state.authReducer.loggedIn,

};

}; // Map Dispatch To Props (Dispatch Actions To Reducers. Reducers Then Modify The Data And Assign It To Your Props)

const mapDispatchToProps = (dispatch) => {

// Action

return {

// Increase Counter

reduxIncreaseCounter: () => dispatch(increaseCounter()),

// Decrease Counter

reduxDecreaseCounter: () => dispatch(decreaseCounter()),

// Login

reduxLogin: (trueFalse) => dispatch(login(trueFalse)),

};

}; // Exports

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

store.js

// Imports: Dependencies

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

import { createStore, applyMiddleware } from 'redux';

import { createLogger } from 'redux-logger';

import { persistStore, persistReducer } from 'redux-persist'; // Imports: Redux

import rootReducer from '../reducers/index'; // Middleware: Redux Persist Config

const persistConfig = {

// Root

key: 'root',

// Storage Method (React Native)

storage: AsyncStorage,

// Whitelist (Save Specific Reducers)

whitelist: [

'authReducer',

],

// Blacklist (Don't Save Specific Reducers)

blacklist: [

'counterReducer',

],

}; // Middleware: Redux Persist Persisted Reducer

const persistedReducer = persistReducer(persistConfig, rootReducer); // Redux: Store

const store = createStore(

persistedReducer,

applyMiddleware(

createLogger(),

),

); // Middleware: Redux Persist Persister

let persistor = persistStore(store); // Exports

export {

store,

persistor,

};

index.js

// Imports: Dependencies

import { combineReducers } from 'redux'; // Imports: Reducers

import authReducer from './authReducer';

import counterReducer from './counterReducer';



// Redux: Root Reducer

const rootReducer = combineReducers({

authReducer: authReducer,

counterReducer: counterReducer,

}); // Exports

export default rootReducer;

authActions.js

// Login

export const login = (trueFalse) => ({

type: 'LOGIN',

trueFalse: trueFalse,

});

counterActions.js

// Increase Counter

export const increaseCounter = () => ({

type: 'INCREASE_COUNTER',

}); // Decrease Counter

export const decreaseCounter = () => ({

type: 'DECREASE_COUNTER',

});

authReducer.js

// Initial State

const initialState = {

loggedIn: false,

}; // Reducers (Modifies The State And Returns A New State)

const authReducer = (state = initialState, action) => {

switch (action.type) { // Login

case 'LOGIN': {

return {

// State

...state,

// Redux Store

loggedIn: action.trueFalse,

}

} // Default

default: {

return state;

}

}

}; // Exports

export default authReducer;

counterReducer.js

// Initial State

const initialState = {

counter: 0,

}; // Reducers (Modifies The State And Returns A New State)

const counterReducer = (state = initialState, action) => {

switch (action.type) { // Increase Counter

case 'INCREASE_COUNTER': {

return {

// State

...state,

// Redux Store

counter: state.counter + 1,

}

} // Decrease Counter

case 'DECREASE_COUNTER': {

return {

// State

...state,

// Redux Store

counter: state.counter - 1,

}

} // Default

default: {

return state;

}

}

}; // Exports

export default counterReducer;

Keep Persisting

And that’s it! Redux Persist is now working in your app and your Redux Store can now be saved on refresh.

No one’s perfect. If you’ve found any errors, want to suggest enhancements, or expand on a topic, please feel free to send me a message. I will be sure to include any enhancements or correct any issues.