- Published on
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;
};
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
Other posts on topic
- Bundle a React library with ParcelCreate a React library and bundle it with the new Parcel v2. Parts of Parcel are rewritten in Rust and that means it is ...Read →
- Understand how styled-components works by creating a cloneFirst article in a guide on how to build your own styled-components clone. Understand why it is necessary and how to sta...Read →
- React Internals (Part 3) - Fiber ArchitectureAn article explaining how Reacts latest Fiber architecture works and speeds up your websiteRead →