paint-brush
Smart Strategy To Create Login Function in React Native Using Powerful Toolsby@skd1993
485 reads
485 reads

Smart Strategy To Create Login Function in React Native Using Powerful Tools

by ShobhitSeptember 27th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

In this post, we will see how an action can be dispatched using Redux on login. The login button should dispatch and action to the reducer which should in turn update the overall app's state. We will use React Native and Redux concepts to create our Login component. We need to set the state from the Login function and set the app state accordingly. For this we need to define some actions in actions in the form of 'What we are doing are what we expect to get back from API call and login page input isLoggedIn'

Company Mentioned

Mention Thumbnail
featured image - Smart Strategy To Create Login Function in React Native Using Powerful Tools
Shobhit HackerNoon profile picture

In this post, we will see how an action can be dispatched using Redux on login, and set the app state accordingly.

Assuming familiarity with React Native and Redux concepts

We have the following Login component to begin with :

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

import CustomButton from '../../components/CustomButton';
import InputField from '../../components/InputField';

import { styles } from './style';

const Login = (props) => {

  // remove these initial assignments after testing
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  return (
    <View>
      <InputField
        placeholder='Enter username'
        value={username}
        onChangeText={(text) => setUsername(text)}
      />
      <InputField
        placeholder='Enter password'
        secureTextEntry={true}
        value={password}
        onChangeText={(text) => setPassword(text)}
      />
      <CustomButton 
        title='Sign In' 
        onPress={() => }  
      />
  );
};

export default Login;

Right now it doesn’t do anything, it’s just barebones UI.

To make it “react” to user action we should update the

onPress
paramter in the
SignIn
button.

<CustomButton title='Sign In' onPress={() => } />

We use

redux
here, so pressing the button should dispatch and action to the reducer which should in turn update the overall app's state.

For sake of simplicity, all redux code is placed in a ‘

redux
’ folder, while the components are in ‘
src/components/<ComponentName>/index.js
’.

This is how our

redux
folder looks like.
redux
├── actions.js
├── actionTypes.js
├── initialState.js
├── reducer.js
└── store.js

Let’s set

initialState
as follows. These are all the fields that our login API will return (yours may differ).

 

userId
and
isLogged
in are flags that we will set on our own (these are not part of API response)

export const initialState = {
  isLoggedIn: false,
  userId: '',
  token: '',
  refreshToken: '',
  expiresOn: '',
  data: '',
};

Define action type in

actionTypes.js

export const SET_LOGIN_STATE = "SET_LOGIN_STATE"

Let’s now create our

loginReducer
in
reducer.js

import { initialState } from './initialState';
import * as t from './actionTypes';

export const loginReducer = (state = initialState, action) => {
  switch (action.type) {
    case t.SET_LOGIN_STATE:
      return {
        ...state,
        ...action.payload, // this is what we expect to get back from API call and login page input
        isLoggedIn: true, // we set this as true on login
      };
    default:
      return state;
  }
};

We can now generate our redux store using all the available information and thunk as middle-ware to handle API calls.

import thunkMiddleware from 'redux-thunk';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly'; // this is for debugging with React-Native-Debugger, you may leave it out
import { loginReducer } from './reducer';

const rootReducer = combineReducers({
  loginReducer: loginReducer,
});

export const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(thunkMiddleware))
);

We have these things in place but we still have not figured how to set the state from the Login component. For this we need to define some actions in

actions.js

What we are looking at is a function that can call the login API and return the result back to us.

Something like :

return fetch(LoginUrl, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(loginInput),
    })
    .then()
...................

But we also need to ensure that the action is “connected” to the “reducer” in order to update the

redux
state or store.

Since API call is considered unpredictable, it should not dispatch the action object directly to reducer, but through a helper.

Dispatching action can return only an Object. If it returns a promise the app will break. We need to make sure of this.

In

actions.js
 :

import * as t from './actionTypes';
import { LoginUrl } from '../constants/Api';

// this is what our action should look like which dispatches the "payload" to reducer
const setLoginState = (loginData) => {
  return {
    type: t.SET_LOGIN_STATE,
    payload: loginData,
  };
};

To fetch this

loginData
in the action above, we create another function using the fetch operation discussed above:

import { Alert } from 'react-native'; // to show alerts in app

export const login = (loginInput) => {
  const { username, password } = loginInput;
  return (dispatch) => {  // don't forget to use dispatch here!
    return fetch(LoginUrl, {
      method: 'POST',
      headers: {  // these could be different for your API call
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(loginInput),
    })
      .then((response) => response.json())
      .then((json) => {
        if (json.msg === 'success') { // response success checking logic could differ
          dispatch(setLoginState({ ...json, userId: username })); // our action is called here
        } else {
          Alert.alert('Login Failed', 'Username or Password is incorrect');
        }
      })
      .catch((err) => {
        Alert.alert('Login Failed', 'Some error occured, please retry');
        console.log(err);
      });
  };
};

You can see how our action is dispatched from this function, which in turn will return a payload object to the reducer in order to perform state update.

Only thing remaining now is connecting this function to the UI. Let’s go back to our

Login
component in the Submit button section and specify
onPress

import { useDispatch } from 'react-redux';
import { login } from '../../redux/actions';

...............

<CustomButton 
        title='Sign In' 
        onPress={() => useDispatch(login({'username': username, 'password': password }))}  
      />
...............

Since we are using

redux
here, all our functions should ideally be in form of some action, which will be caught in the
redux-thunk
middleware first and then passed on appropriately to reducer.

On successful login, the

initialState
values will all be populated. On failure, an alert will show up stating error.

I hope this was helpful & informative :)

Also published at https://dev.to/skd1993/creating-a-simple-login-function-with-redux-and-thunk-in-react-native-33ib