React Context API + useReducer() = Redux
Tags: react, redux, hooks
Published: 2020-05-26 06:28:49

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;
};
```
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>
  );
};
```

We can now export this
```
export { AuthProvider, AuthContext };
```

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>
    );
  }
}
```

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>
  );
};
```

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](https://twitter.com/burhanuday). Subscribe to my [newsletter](https://www.burhanuday.com/)