Search
  • Sotiris Karapostolou

React Native — React Navigation with Redux Saga

Updated: Nov 23

So, Im working on user authentication functionality on our mobile app. That is when a user logs in successfully, we direct them to their Catalogue page.This is where I came across to this #issue #how to use navigation outside a functional component.


Let me guide you though.


First we gather all the user's data with the help of our custom Formik under my services folder (maybe I'll go thouht this topic at some later date too - subscribing to me right now seems like a good idea, doesn't it ;)! )



#Login.js
...

import { MiniFormik, FormButton, Field } from 'services/MiniFormik';

const UserForm = () => {

  const dispatch = useDispatch();
  const { loading } = useSelector((state) => state.auth);
 
return (
   <CustomFormik
     initialValues={{ email: '', password: '' }}
     validationShema={yup.object().shape({
       email: yup.string().email().required(),
        password: yup.string().required(),
     })}
      onSumbit={({ values }) => {
       const { email, password } = values;
       dispatch(signinStart({ email, password }));
     }}
   >
     <Container style={styles.container}>
      <View style={styles.form}>
        <View style={styles.formControl}>
          <Field name="email" focusNext="password" />
          <Field name="password" secureTextEntry />
         </View>
          <View style={styles.buttonContainer}>
           <FormButton loading={loading} title="Login" />
         </View>
       </View>
     </Container>
   </CustomFormik>
  );
};


Then we send the payload to redux sagas by dispatching the respective action. and we

let AWS Amplify to handle the user Authentication.


After Amplify send us success we navigate the user to the Catalogue with the help of our NavigationService functions under my services folder. As you see so far having a services folder is a must to every project ( I guess app structuring would be also one of my next storytell... )


#sagas.js
...

import { call, put } from 'redux-saga/effects';
import { Auth } from 'aws-amplify';
import * as NavigationService from 'services/NavigationService';

export function* onSigninAsync({payload: {formData: { email, password },}, }) {
 
try {
  const { username, attributes } = yield Auth.signIn(email, password);
  yield put(signinSuccess({username, name: attributes.name,
   email: attributes.email,}));
   
  NavigationService.navigate('Catalogue');
  
} catch (err) {
  // AWS Amplify is so awesome that they even provide basic and useful details like when you attempt to sign in with a wrong user name, or a user name that does not exist, an error message is displayed.
  }
}

Ok, let's take a look at what this NavigationService.js file is.


#services/navigation/NavigationService.js

import { NavigationActions } from 'react-navigation';

const config = {};
export function setNavigator(nav) {
 if (nav) {
 config.navigator = nav;
  }
}
export function navigate(routeName, params) {
 if (config.navigator && routeName) {
 let action = NavigationActions.navigate({ routeName, params });
 config.navigator.dispatch(action);
  } 
}
  
export function goBack() {
 if (config.navigator) {
 let action = NavigationActions.back({});
 config.navigator.dispatch(action);
  }
}

So the first function setNavigator(nav) is the one that initialize the navigation prop reference

into the config oblject. We do that by calling this function in our App.js file and passing a ref, we'll get to that in a bit.


Now that we have the navigation prop reference we can dispatch any action we need. In the first case:

  • navigate(routeName, params): This function will take the screen name and navigate to that screen when used as well as pass any params we want to pass down to that navigation.

  • goBack(): This function goes back to the previous screen

Similarly we can create a replace or a reset function


#services/navigation/NavigationService.js

import { NavigationActions, StackActions } from 'react-navigation';


export function replace(routeName, params) {
 if (config.navigator && routeName) {
  let action = StackActions.replace({ routeName, params });
  config.navigator.dispatch(action);
  } 
}

export function reset(routeName, params) {
 if (config.navigator && routeName) {
  let action = StackActions.reset({
  index: 0,
  actions: [NavigationActions.navigate({ routeName, params })],
    });
 config.navigator.dispatch(action);
  }
}

  • replace(routeName, params): This function replace the current route with a new one

  • reset(routeName, params): This function wipe the navigator state and replace it with the result of several actions


Finally the last horse in the race:


#App.js
...
import React, { useRef, useEffect } from 'react';
import * as NavigationService from 'services/NavigationService';
import MainNavigator from './router';
import store from 'store';

const App = () => {
 const navRef = useRef();
 
 useEffect(() => {
 if (navRef.current)
   NavigationService.setNavigator(navRef.current);
  }, [navRef.current]);

 return (
    <Provider store={store}>
      <MainNavigator ref={navRef} />
    </Provider>
  );
};

Well, I hope that helped! Right to the point! If you like this and want to support me on making more stories you can contact me on Linkedin


#reactNative #reactNavigation #redux #reduxSagas #AWSamplify #javascript


©2020 by CodeMasters and Skilltransfers