The Context API in React is a powerful feature that allows you to manage state globally across your application without the need to pass props down through multiple levels of your component tree. When combined with TypeScript, it becomes even more robust, providing type safety and better code maintainability. In this article, I'll walk you through setting up a Context API for authentication in a React app using modern conventions and TypeScript.
Project Structure
First, let's outline the project structure. We'll split our Context setup into four separate files:
context.tsx: This file will create and export the context.
provider.tsx: This file will provide the context to the component tree.
reducer.ts: This file will define the initial state and reducer function.
actions.ts: This file will contain action creators.
state.ts: This file will contain the initial state of the context
useAuthContext.ts: This file contain a custom hook which will help to call context
01. Creating the Context
We'll start by creating the context.tsx file. This file will initialize the context and provide a custom hook for easy access.
import { createContext } from "react";
import { AuthContextProps } from "../types";
export const AuthContext = createContext<AuthContextProps | undefined>(undefined);
02. Defining the Initial State of the Context
After that we will define the initial state for the context in the state.ts. This initial state will be use for storing all the datas.
import { AuthState } from "../types";
export const initialState: AuthState = {
isAuthenticated: false,
user: null,
token: null,
};
03. Setting Up the Provider
Next, we'll set up the provider in the provider.tsx file. This component will use the useReducer hook to manage the state and pass it down via the context provider.
import React, { useReducer } from "react";
import { AuthProviderProps } from "../types";
import { AuthContext } from "./context";
import { AuthReducer } from "./reducer";
import { initialState } from "./state";
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [state, dispatch] = useReducer(AuthReducer, initialState);
return <AuthContext.Provider value={{ state, dispatch }}>{children}</AuthContext.Provider>;
};
04. Defining the Reducer
The reducer is the heart of our state management. In the reducer.ts file, we'll define our initial state and the reducer function to handle actions.
import { AuthAction, AuthState } from "../types";
export const AuthReducer = (state: AuthState, action: AuthAction): AuthState => {
switch (action.type) {
case "LOGIN":
return {
...state,
isAuthenticated: true,
user: action.payload.user,
token: action.payload.token,
};
case "LOGOUT":
return {
...state,
isAuthenticated: false,
user: null,
token: null,
};
default:
return state;
}
};
05. Creating Actions
Action creators make it easier to dispatch actions in a type-safe way. We'll define these in the actions.ts file.
import { AuthAction, IUser } from "../types";
export const login = (user: IUser, token: string): AuthAction => {
return {
type: "LOGIN",
payload: { user, token },
};
};
export const logout = (): AuthAction => {
return { type: "LOGOUT" };
};
06. Configure the custom-hook.
This custom will help use set and call the context in any components without involving multiple parameters into it. Create a file and name it useAuthContext.ts.
import { useContext } from "react";
import { AuthContext } from "./context";
export const useAuthContext = () => {
const context = useContext(AuthContext);
if (!context) throw new Error("Error: useAuth must be within in AuthProvider");
return context;
};
We are set with all the initial configuration for the state management; now we will see how we can utilize this in our application.
Using the Context
To utilize our new AuthContext, we need to wrap our application (or part of it) in the AuthProvider. We'll do this in our main entry point, typically App.tsx.
import React from "react";
import { AuthProvider } from "./context/provider";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./Home";
const App: React.FC = () => {
return (
<AuthProvider>
<Router>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</Router>
</AuthProvider>
);
};
export default App;
Within any component, we can now use the useAuth hook to access the auth state and dispatch actions. Here's an example component that uses our AuthContext:
import React from "react";
import { useAuthContext } from "./context/useAuthContext";
import { IUser } from "./types";
import { login, logout } from "./context/actions";
const Home: React.FC = () => {
const { state, dispatch } = useAuthContext();
const handleLoginClick = () => {
const user: IUser = { firstName: "John", lastName: "Doe", role: "SUPER-ADMIN" };
const token = "dfs56ds56f5.65sdf564dsf.645sdfsd4f56";
dispatch(login(user, token));
};
const handleLogoutClick = () => dispatch(logout());
return (
<div>
<h1>Home Page</h1>
{state.isAuthenticated ? (
<div>
<h3>Welcome {state.user?.firstName}</h3>
<button onClick={handleLogoutClick}>Logout</button>
</div>
) : (
<button onClick={handleLoginClick}>Login</button>
)}
</div>
);
};
export default Home;
Types & Interfaces
import { Dispatch, ReactNode } from "react";
export interface IUser {
firstName: string;
lastName: string;
role: "SUPER-ADMIN" | "ADMIN" | "USER";
}
export interface AuthState {
isAuthenticated: boolean;
user: null | IUser;
token: null | string;
}
export interface AuthContextProps {
state: AuthState;
dispatch: Dispatch<AuthAction>;
}
export interface AuthProviderProps {
children: ReactNode;
}
export type AuthAction =
| { type: "LOGIN"; payload: { user: IUser; token: string } }
| { type: "LOGOUT" };
Conclusion
By following this structured approach, you can manage global state in your React applications more effectively. The Context API, when used with TypeScript, provides a powerful and type-safe solution for state management. This setup is not only limited to authentication but can be adapted for other use cases like theme management, language settings, and more.
With this knowledge, you can now use Context-API like a pro! Feel free to modify and extend this setup to fit the needs of your own projects.
Top comments (0)