DEV Community

Burhanuddin Udaipurwala
Burhanuddin Udaipurwala

Posted on • Edited on • Originally published at burhanuday.com

React Context API + useReducer() = Redux

Redux is a state management solution for web applications. Although it is widely used with React, it can be used with any Javascript app. Although Redux is a great state management solution, it is boilerplate-y in nature and adds to the overall size of your app.

React is a UI library that does not ship with its own state management solution - or does it?

React Context API

In a typical React application, data is passed top-down (parent to child) via props, but this can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.

On its own, the context api is not a substitute for Redux since you cannot replicate the complex action-reducer paradigm without other hooks

Disclaimer

A word of disclaimer before we start. I would suggest that the method i am documenting here only be used in small projects. If you are building something huge, you should still use Redux. It provides a lot more functionality through Thunks, Saga and Reselect.

The solution

Sharing the auth state with all the components of your component tree is a common usecase. Let's implement that using the context api and the useReducer hook.

Use the useReducer hook to create a reducer function

import React, { useReducer } from "react";

const initialState = {
  user: null,
};

export const AUTH_STATE_CHANGED = "AUTH_STATE_CHANGED";

const reducer = (state, action) => {
  switch (action.type) {
    case AUTH_STATE_CHANGED:
      return {
        user: action.payload,
      };
  }
  return state;
};
Enter fullscreen mode Exit fullscreen mode

I have created a simple reducer function similar to what you would see in a Redux project by passing the hook a reducer function and an initial state.

Using the React Context API, we can now create a context that we want to drill down the app. The authState object is the state that you want to be passed down to your components and actions object contains all the actions that you would typically use with Redux. The useReducer hook returns a dispatch function just like Redux

const AuthContext = React.createContext();

const AuthProvider = (props) => {
  const [authState, dispatch] = useReducer(reducer, initialState);

  const actions = {
    authStateChanged: (user) => {
      if (user) {
        dispatch({ type: AUTH_STATE_CHANGED, payload: user });
      } 
    },
  };

  return (
    <AuthContext.Provider
      value={{
        authState: authState,
        authActions: actions,
      }}
    >
      {props.children}
    </AuthContext.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

We can now export this

export { AuthProvider, AuthContext };
Enter fullscreen mode Exit fullscreen mode

Wrap the component you want to access the state from. Since i want to be able to access the authState from anywhere in my app, I will wrap my App component. If you do not want the whole app to be able to access the state, you can scope the state by selectively wrapping the components that need to be able to access the state

import { AuthProvider } from "./authContext";

export default function App() {
    return (
      <AuthProvider>
        <Login />
      </AuthProvider>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Now to access the state from any component inside my app eg. Login screen

import { AuthContext } from "./authContext";
const Login = (props) => {
  const { authState, authActions } = React.useContext(AuthContext);

  const login = () => {
    authActions.authStateChanged({ name: "Burhanuddin" });
  }

  return (
    <div>
      {authState.user.name}
      <button onClick={() => login()}>
        Login
      </button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

With this you can replicate Redux inside React without any external dependencies

Connect with me

You can follow me on DEV or connect with me on Twitter. Subscribe to my newsletter

Top comments (3)

Collapse
 
theluk profile image
Lukas Klinzing

If you have as many actions as usually you have in redux this might get quite expensive as every change will recreate all those actions and all components that depend on any of the action or any of the state. That is a fundamental difference between redux and context. Redux handles memoization well and selects only what you need. Also you create actions only once and not multiple times. So because the dispatch function never changes, so should also the actions never change.

Collapse
 
kambleaa007 profile image
Ashish Kamble

so what will be better efficient way of using context in enterprise projects

Collapse
 
burhanuday profile image
Burhanuddin Udaipurwala

Use it for state that does not change often - stuff like theme preference, language preference. Context is not a state management solution, it's a dependency injection mechanism