Should I totally ditch redux API for context API?

Should I totally ditch redux API for context API?

Introduction

If you're like me, immediately you learned how to use react hooks and the context API (useContext) you'd have stopped using redux for your app(web/mobile) state management.

driving off.gif

But, the challenges I faced this week at work thought me otherwise.

Redux

According to the Redux website

Redux is a predictable state container for JavaScript apps. It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time-traveling debugger. You can use Redux together with React, or with any other view library. It is tiny (2kB, including dependencies), but has a large ecosystem of addons available. redux API explanation context API - use cases(like the one used for auth).

I am a react developer so I use redux with react. Now, this is my personal experience with redux but it was not easy to learn, it felt...very complicated, and most times I found myself "googling" how to do this and how to do that. Then there was the very much dreaded MapStateToProps.

nervouse.gif

Suffice to say redux feels like a lot to deal with especially if you're new to react. Here comes the

Context API

Using Context API(a combination of the useContext and useReducer hooks) for state management is - compared to redux - quite easy. All you had to do was:

  • create a file, call it "context.js" import createContext from the react library, call the function and assign it to a variable, and export it:
import { createContext } from 'react';

const AppContext = createContext();

export default AppContext;
  • In your app.js import the AppContext module:
import AppContext from './context.js';

const App = ()=>{
  return (
    <div>
      {/* some code */}
    </div>
  )
}
  • create a file 'appReducer.js' which exports a reducer and its initial state:
export const initialState = {
  isLoading: true,
  userID: null,
  accessToken: null,
};

export const appReducer = (prevState, action) => {
  switch (action.type) {
    case 'RETRIEVE_TOKEN':
      return {
        ...prevState,
        accessToken: action.token,
        isLoading: false,
      };
    case 'LOGIN':
      return {
        ...prevState,
        userID: action.id,
        accessToken: action.token,
        isLoading: false,
      };
    case 'LOGOUT':
      return {
        ...prevState,
        userID: null,
        accessToken: null,
        isLoading: false,
      };
    case 'REGISTER':
      return {
        ...prevState,
        userID: action.id,
        accessToken: action.token,
        isLoading: false,
      };
    case 'LOADING':
      return {
        isLoading: true,
      };
    case 'NOTLOADING':
      return {
        isLoading: false,
      }
  }
}
  • create a reducer using useReducer in the "App.js" file. useReducer accepts two arguments, a function which is the reducer, and the initial state of the reducer which is an object and returns the current state, and a dispatch function that would be used to dispatch actions. While importing your "initialState" and "appReducer" into your "App.js" file.
import AppContext from './context.js';
import { appReducer, initialState } from './appReducer.js';

  const [state, dispatch] = useReducer(appReducer, initialState);
  • Wrap your component in the provider form the context:
import AppContext from './context.js';
import { appReducer, initialState } from './appReducer.js';

const [state, dispatch] = useReducer(appReducer, initialState);

const context = useMemo(()=>{
  //use state and dispatch to carry out actions here
  //you can also use it to store simple variables.
})

const App = ()=>{
  return (
    <AppContext.Provider value={context}>
      <div>
        {/* some code */}
      </div>
    </AppContext.Provider>
  )
}

You can store anything in the context object and it would be available to you anywhere in your app (if you wrap your root code with the App.context.Provider) and access them using:

const {anythingInContextObject} = useContext(AppContext}

It's such an easy and straightforward way of managing state, at least it is for me. So why then shouldn't you totally leave redux?

Thoughts

My answer would be it depends on your use case and how complex your app is. I started out using context API to manage data on the project I'm currently working on and I realized after a while that I needed to use redux. This was a special use case.

Special because it wasn't just a react app, it was a next.js app, which as we know is server-side rendered.

The downside of using the context API is it has to be used in a functional component and nothing more, you cannot use react hooks(useContext) any other way and because this was a server-side rendered application, I had to use a Higher-order component to check authentication before rendering a page to the client-side. So if you have a large-scale application that would most likely require you to use your global state anywhere that isn't a functional component, you might want to use redux instead.

Conclusion

Use cases matter, if you are working on a small project it might be easier and faster for you to use the context API, you can also use context API with redux. It is my opinion that we shouldn't totally forget about redux, it is a very useful complicated tool, and it's always a plus having it as a skill in your quiver.