A More Practical useContext

React Hooks

You may have read my earlier article about useContext and know what it is and how it’s used. However, that article was more on the concept than actual practical use. We know that useContext provides a way to share data between components without having to pass it down through multiple layers of props. By using useContext you can access the values stored in the context object and update them from within your components. This can simplify your code and make it more efficient by avoiding unnecessary props drilling.

There are three steps in creating and using useContext

Step 1: Create a context file that also includes a provider, and create a value object that you want other components to use

Step 2: Update the App.ts file to use the Provider

Step 3: Test and use the context in other components

About the Context we are creating

We will create a context called UserContext that will have an object of the current user logged in. On the page, the user can click a Login button to log in, and the login functionality will pick one from the four users we hard-coded in the context. After logging in, the page will show the info about the user. If you click the Logout button, the current user object will clear and you can re-login again.

Let’s now create the context file

import React, {
  useState,
  createContext,
  useContext,
  Dispatch,
  SetStateAction
} from "react";

/*
  Create and Initialize a User Context
*/
export const UserContext = createContext({});

/*
  Create a useUser so components can use the context
*/
export function useUser() {
  return useContext(UserContext);
}

/*
  Type definition for user data, children, and return type
*/
type UserProps = {
  name: string;
  role: string;
};

type UserProviderProps = {
  children: React.ReactNode;
};

export type ReturnType = {
  user: {};
  setUser: Dispatch<SetStateAction<UserProps[]>>;
  login: () => void;
  logout: () => void;
};

/*
  Set list of users
*/
const users = [
  { name: "Jack Davidson", role: "admin" },
  { name: "Cindy Anderson", role: "admin" },
  { name: "Bob Smith", role: "user" },
  { name: "Phil Case", role: "user" }
];

/*
  Context's Provider, this defines and "provides" data to the consuming comonents
*/
export const UserProvider = ({ children }: UserProviderProps) => {
  const [user, setUser] = useState({});

  const login = () => {
    if (Object.keys(user).length === 0) {
      setUser(users[Math.floor(Math.random() * users.length)]);
    }
  };
  const logout = () => {
    setUser({});
  };

  const returnValues: ReturnType = {
    user,
    setUser,
    login,
    logout
  };

  return (
    <UserContext.Provider value={returnValues}>{children}</UserContext.Provider>
  );
};

We first import a few things from react. You should already be familiar with the useState hook which sets the application states. The two context hooks, createContext and useContext, are used to create and use the context we have here. When creating a new context, it is ideal to set an initial value to it even when it’s empty. The context action type, SetStateAction, is a type definition used for setting the state variables.

The next three blocks of code set the type of the user (UserProps), children (UserProviderProps), and return type (ReturnType). The UserProps contains the data we are using, which are name and role, of the user. The UserProviderProps is a type for the children in our provider, and the ReturnType contains all the variables and functions we are returning from the context and to be consumed by various components.

We are passing four things from the context. The user and setUser are from the state, login and logout are the two functions to log in and log out users, and we are returning everything in the last return block.

Use the Provider in the App.tsx

Now that we have created the context file, we need to have a way that various components can see and use the context. This is done by adding the provider in App.tsx

import { UserProvider } from "./context/userContext";
import { Mypage } from "./components/Mypage";

import "./styles.css";

export default function App() {
  return (
    <UserProvider>
      <h1>Hello Coder</h1>
      <Mypage />
    </UserProvider>
  );
}

In App.tsx, we first import the UserProvider from our context file, then in our return block we wrap everything with the <UserProvider>…</UserProvider>. This way every component will be able to consume to data provided by UserProvier, or the UserContext file above.

Use the Context data in a component

Now that we have created the context file and set up App.tsx with its provider, we can use the data or functions in our components. Remember in our context file we returned four things, user, setUser, login, and logout. We can use them now

import React from "react";
import { useUser } from "../context/userContext";
import { ReturnType } from "../context/userContext";

export const Mypage = () => {
  const { user, login, logout } = useUser() as ReturnType;

  console.log("users", user);

  return (
    <>
      <h2>My Page</h2>
      <button onClick={login}>Login</button>
      <button onClick={logout}>Logout</button>
      <br />
      <br />
      User: {JSON.stringify(user)}
    </>
  );
};

In this file we first import “useUser” from the context file which lets us use the UserContext. We are also importing ReturnType which we are using to type the data returned from the context. Once they are all imported, we can use the data (user) and functions (login and logout) in our component.

The output

This is when no user has logged in, or after you click the logout button

When you click the Login button, it will grab one of the four users and set it as the current user. This is normally the place where you require the user to log in, have it authenticated, and display their info on the page.

You Might Also Like