Not Logging in using Redux-persist
Redux persist takes object from your Redux state and saves it to persisted storage.
Installation
npm install --save redux-persist
In my project, I will be using redux-thunk as a middleware for configuring the redux store in my app.
First, I am going to make two pages. First one will have a form which will have two fields — username and password and a submit button which will submit the form and navigate us to another page. In my application, I will be using react-native-router-flux for navigation.The routes are defined like this-
import React from ‘react’;import {Actions, Router, Scene, Stack, Lightbox, Reducer,} from ‘react-native-router-flux’;import Root from ‘./index’;import FirstPage from ‘./FirstPage’;export default class Routes extends React.Component{ render(){ return( <Router> <Lightbox key=”modal”> <Scene> <Scene key=”root” component={Root} intial/> <Scene key=”firstpage” component={FirstPage}/> </Scene> </Lightbox> </Router> ) }}
Here, Root is the component which contains the form and the FirstPage is the component where we will be navigated to after clicking the button.
Root component will look like this —
import React from ‘react’;import {connect} from ‘react-redux’;import { StyleSheet, Text, View, TextInput, Button } from ‘react-native’;import FirstPage from ‘./FirstPage’;import { Actions } from ‘react-native-router-flux’;class Root extends React.Component{onPressButton = () => {this.props.loginUser();}render(){return (<View style={{flex: 1, alignItems: ‘center’, justifyContent: ‘center’}}><Text>Hello</Text><TextInputvalue=”aayushisinha”style={{ borderWidth: 1, margin: 5, padding: 15 }}/><TextInputvalue=”password”style={{ borderWidth: 1, margin: 5, padding: 15 }}/><Button title=”Sign In” onPress={this.onPressButton}/></View>)}}export default Root;
Here, it has two input boxes and a button for navigation on click. And in firstPage.js, I have
import React from ‘react’;import {View, Text} from ‘react-native’;const FirstPage = () => (<View style={{flex: 1}}><Text>Hello there</Text></View>)export default FirstPage;
For not making the user log in everytime he/she opens the app, we will be using Redux-persist.
For configuring the store, I have created a separate file config-store.js.
The code for it looks like this —
import { createStore, applyMiddleware } from ‘redux’;import { persistStore, persistReducer } from ‘redux-persist’;import storage from ‘redux-persist/lib/storage’; // defaults to localStorage for web and AsyncStorage for react-nativeimport thunk from ‘redux-thunk’;import autoMergeLevel2 from ‘redux-persist/lib/stateReconciler/autoMergeLevel2’;import rootReducer from ‘./reducers/index’;const persistConfig = {key: ‘root’,stateReconciler: autoMergeLevel2,storage,whitelist: [‘app’], // only app will be persisted};const persistedReducer = persistReducer(persistConfig, rootReducer);const configStore = async () => {const store = createStore(persistedReducer, applyMiddleware(thunk));const persistor = persistStore(store);return { persistor, store };};export default configStore;
persistStore does both persistence and restore.It sets up a listener that will continuously saves newly changed state to storage.
persistReducer is notified of the changes that happens in the store. The process of persisting a change is asynchronous and happens in the background whenever there is any change in the store.
Note that: Since I am using combineReducers in my reducer, that’s why I am using persistReducer during configuration. Had it been raw reducers, I would have used persistCombineReducers in configuration.
storage defaults to localStorage in case of web apps and AsyncStorage for mobile apps.
There are two levels of merging that can be done in redux persist.
autoMergeLevel1 acts as the default level of PersistReducer. It replaces the top level state with whatever data was persisted.
autoMergeLevel2 means that the merge process will first make a copy of the initial states, and then only override the keys within the object that were persisted.
whitelist/blacklist are optional arguments. The keys in whitelist are persisted by the store and the keys in blacklist are discarded by the store. By default, all the reducers in the rootReducer are persisted if nothing is specified.
We will be importing this file in the file that will be launched firstly on opening of the app.(Eg: App.js is the first file to be loaded when we create a project using create-react-app or create-react-native-app).
In App.js, we will be configuring the store and importing the routes.
import React from ‘react’;import {Provider} from ‘react-redux’;import Routes from ‘./routes’;import configureStore from ‘./config-store’;import { PersistGate } from ‘redux-persist/integration/react’;export default class App extends React.Component {constructor(props) {super(props);this.state = {storeCreated: false,store: null,};}componentDidMount() {configureStore().then(({ persistor, store }) =>this.setState({ persistor, store, storeCreated: true }));}render() {if (!this.state.storeCreated) return null;return (<Provider store={this.state.store}><PersistGate persistor={this.state.persistor}><Routes/></PersistGate></Provider>);}}
We want our app to load only when our state is completely configured. So, we will be taking a boolean state variable for store for checking whether it is configured or not.
Now in actions of redux-thunk, we will have a function which will set the value of a variable as true when a user logs in.
actions/index.js
export const loginUser = () => ({type: ‘LOGIN_USER’,})
You can do your required api calls for login here.
In reducer, I have a variable isLoggedIn, whose value we will be altering after doing the login API call.
const initialState = {
isLoggedIn: false,
}export default (state=initialState, action) => {switch(action.type){case “LOGIN_USER”:return {…state,isLoggedIn : true}default:return state}}
Now, we will have to dispatch this action on the click of our submit button. We will connecting our store to the Root component like this —
const mapDispatchToProps = {loginUser};const mapStateToProps = ({app}) => ({app});export default connect(mapStateToProps, mapDispatchToProps)(Root);
Since, the props value will be changing so, we will have to use lifecycle method componentWillReceiveProps in this way.
componentWillReceiveProps(nextProps){if(nextProps.app.isLoggedIn){Actions.firstpage();}}
Since we have used redux persist, after the first login the value of isLoggedIn will be stored in the store.
So, for the next time when user opens the app, we will check the value of isLoggedIn when the component mounts.
componentDidMount(){if(this.props.app.isLoggedIn){Actions.firstpage();}}
So, the whole code will be like this
import React from ‘react’;import {connect} from ‘react-redux’;import { StyleSheet, Text, View, TextInput, Button } from ‘react-native’;import {loginUser} from ‘./actions/index’;import FirstPage from ‘./FirstPage’;import { Actions } from ‘react-native-router-flux’;class Root extends React.Component{componentWillReceiveProps(nextProps){if(nextProps.app.isLoggedIn){Actions.firstpage();}}componentDidMount(){if(this.props.app.isLoggedIn){Actions.firstpage();}}onPressButton = () => {this.props.loginUser();}render(){return (<View style={{flex: 1, alignItems: ‘center’, justifyContent: ‘center’}}><Text>Hello</Text><TextInputvalue=”aayushisinha”style={{ borderWidth: 1, margin: 5, padding: 15 }}/><TextInputvalue=”password”style={{ borderWidth: 1, margin: 5, padding: 15 }}/><Button title=”Sign In” onPress={this.onPressButton}/></View>)}}const mapDispatchToProps = {loginUser};const mapStateToProps = ({app}) => ({app});export default connect(mapStateToProps, mapDispatchToProps)(Root);
You can see the repository for this by clicking here.
That’s all.