React Context is good enough
Tags: react, javascript, reactnative
Published: 2020-06-20 20:19:57

I'm a heavy Redux user and quite a fanboy of the library itself as well to a point I also dabbled into [Redux Toolkit](https://redux-toolkit.js.org/ "Redux Toolkit") which is the Redux's team opinionated way of using the library efficiently and tried it out by using it on a production application. (I took the risk but hey! things went well!)

# Redux's advantage points

It is a solid state manager for React. It works out of the box and does everything that you expect from it to do not to mention that it is configurable with various plugins created by the community to make your work easier as well.

# Redux's disadvantage points

A lot of people talk about Redux's setup that they have to write a good amount of boilerplate code to make it work, although that is true it is solvable when using Redux Toolkit since it offers a preconfigured setup with redux-thunk and setup with the Redux DevTools extension out of the box.

My biggest issue with Redux is that using other library's hooks is quite a pain, take an example of [React Toastify](https://fkhadra.github.io/react-toastify/introduction/ "React Toastify") library which offers a hook to show a toast on your website (a small popup). If I want to use that library along with Redux then either I have to pass it along with my dispatch function or create a Context component that uses the **useSelector** hook inside of it and use that hook to make a popup when a user inserts wrong information (or maybe I have been using Redux incorrectly and I have found a proper way to handle this situation)

# Why not use Context in the first place?

Come to think of it, React does offer a decent amount of hook functions that do the job as expect and the best part: you don't have to install other libraries for state management since you already have one built-in for free!

The best part about Context is that you get to use all of React's cool features out of the box and use other hooks inside of it without any issue at all.

The worst part is that you will have to manage multiple Contexts inside of your app (you could use one Context just like what Redux does but I prefer to split things down into small manageable chunks).

# A small example

Here I will be offering a small example using React Native and React Navigation to check whether a user is logged in or not

    import React, {useState, useEffect, createContext, useContext} from 'react';
    import AsyncStorage from '@react-native-community/async-storage';
    import {useNavigation, CommonActions} from '@react-navigation/native';
    
    const checkIfLoggedIn = async () => {
    	const token = await AsyncStorage.getItem('token');
        if(token !== null) {
        	return true;
        }
        return false;
    }
    
    const flushToken = async () => {
    	await AsyncStorage.clear();
    }
    
    export const AuthContext = createContext(null);
    
    const AuthProvider = ({children}) => {
      const [authorized, updateAuthorization] = useState(false);
    
      const navigation = useNavigation();
    
      useEffect(() => {
        checkIfLoggedIn().then((response) => {
          if (response) {
            navigation.navigate('App');
            updateAuthorization(true);
          } else {
            navigation.navigate('Auth');
          }
        });
      }, [authorized]);
    
      const logout = async () => {
        await flushToken();
        navigation.dispatch(
          CommonActions.reset({
            index: 0,
            routes: [{name: 'Auth'}],
          }),
        );
        updateAuthorization(false);
      };
    
      return <AuthContext.Provider value={logout}>{children}</AuthContext.Provider>;
    };
    
    const useLogout = () => {
      const context = useContext(AuthContext);
      if (context === null || context === undefined) {
        throw new Error('useLogout must be inside of the AuthProvider');
      }
      return context;
    };
    
    export {AuthProvider, useLogout};

### The code does the following

When the user opens the app, it checks if there is a token saved in the device's localStorage first. If there is a token then it navigates to the App Stack, if not then it takes the user to the Auth Stack so that they can sign up or log in.

Inside that Context is a logout function which what it does is that it sets the authentication state to false and triggers the **useEffect** hook which navigates the user back to the Auth Stack and removes the previous history so that they don't go back (some Android phones have a back button, if you didn't clear your navigation stack then they can go back to the App Stack even though they are logged out) and deletes your token from the phone.

I also created a **useLogout** hook which lets me to logout in any component that is encapsulated within that AuthProvider component (handy for later use) which relies on the logout function that is inside of my AuthProvider (inside of my AuthContext).

See that I have relied on **useNavigation**, a hook from the React Navigation library and it was easily integrated inside of my application. 

### You can do all of this in Redux as well

Yes, you can do a similar action with Redux but the key difference is that you have to add a reducer in your store and create some actions to dispatch an action for logout and logging in not to mention that you need to find a way to pass the **useNavigation** hook function to navigate the user to a different screen.

### The key difference

Here I made things a bit simpler for me to debug not to mention that if I wanted to add a different hook function or send an API function whenever I logout then it is easily done with the Context example. This way I encapsulate each functionality with its own state and keep things modular rather than sticking everything in one store and one place.