Exmaple with useContext in React JS functional components

Exmaple with useContext in React JS functional components

In this brief article, we will discuss the effective implementation of useContext. For those unfamiliar, useContext is a technique for sharing state within an application between various components, without requiring the passing of parameters from parent components to each child component. This approach facilitates component modularization and eliminates the need for additional properties that are only passed to child components.

As an example, let’s consider a scenario where a search option is located in the header and we want to apply filters to items displayed on the active screen. The active screen may change dynamically as the user navigates through the application, displaying a list of products at one time and a list of users at another. Using a traditional approach, we would have a state at the application root component and pass the necessary values and handler functions to each child component. However, this creates an unnecessary dependency on intermediate components that may only apply to the current application, limiting the component’s reusability in other contexts where the additional property is not required.

By using useContext, we can set a context at a higher level and access it from components without having to pass data through the properties. This approach is similar to using Redux, but useContext comes built-in with ReactJS and offers simpler state sharing. While Redux is more suitable for complex data models, useContext is better suited for sharing simpler states.

Now that we have introduced the concept of useContext, let’s dive into how we can implement it in a ReactJS application. The implementation of useContext typically involves the following steps:

  1. First, create a context using the createContext method. This method returns an object that contains a Provider component and a Consumer component.
  2. Next, wrap the application with the Provider component at the appropriate scope where we want to share the context. This can be done in the highest level component that needs access to the shared state.
  3. Finally, use the useContext hook to access the variable and the function to manage the variable. This allows components nested within the Provider to access the shared state without having to pass down the state as props.

Overall, implementing useContext can simplify the management of state in a ReactJS application, reducing the amount of props drilling and improving the modularity and reusability of components.

To begin, we will define a context object for search using TypeScript. Please note that I will be using TypeScript for type annotations in this example, but if you are using plain JavaScript, you can omit the type annotations.

Creating the Context

//FileName: SearchContext.ts

import React, { createContext } from "react";

export interface searchInterface {
  search: string;
  setSearch: (search: string) => void;
}

const SearchContext = createContext<searchInterface>({
  search: "",
  setSearch: () => {},
});

export default SearchContext;

In the above code we have created a context using the createContext method.

Using the Provider wrapper to share Context

As the second step, we will wrap the components by the context provider to share the context with the child components.

//FileName: App.tsx

import React, { useState } from 'react';
import SearchContext from './component/SearchContext';
import Products from './Product/Products';

const App = () => {
    const [search, setSearch] = useState("");
    return (
        <div>
           <SearchContext.Provider value={{ search, setSearch }}>
            <Routes>
                <Route path="/" element={<Home />} />
                <Route path="/login" element={<LoginPage />} />
                <Route path="/products" element={<Products />} />
                <Route path="/products/:productId/settings" element={<Product />} />
                <Route path="/products/new" element={<Product />} />

                <Route path="/orders" element={<Orders />} />
                <Route path="/orders/:orderId/settings" element={<Order />} />
                <Route path="/shops" element={<Shops />} />
                <Route path="/shops/:shopId/settings" element={<Shop />} />
                <Route path="/users" element={<Users />} />
                <Route path="/users/:userId" element={<UserView />} />
                <Route path="/users/:userId/settings" element={<User />} />
                <Route path="/settings" element={<User />} />
          </Routes>
        </SearchContext.Provider>
        </div>
    );
};

export default App;

You can avoid most of the codes in the above example and the important part of the code is we have created an state for search function as below.

    const [search, setSearch] = useState("");

After that we have wrapped the child components using search context provider as shown below.

 <SearchContext.Provider value={{ search, setSearch }}>
            //All the components share the context goes here
 </SearchContext.Provider>

We have also imported the SearchContext in the top of the App.jsx file as you can see in the above code.

Using the context to access and update the value

Once we have the provider in place we can access the context in the child component as shown below. Here is the code from Header component where we update the search value in the context.

//File Name: header.tsx

import React, { useState, useEffect, useContext } from "react";
import SearchContext from "./SearchContext";

const Header = () => {
  const [SearchText, setSearchText] = useState("");
  const searchContext = useContext(SearchContext);
  return (
    <header>
        <div>
          <input
            value={SearchText}
            onChange={(e) => {
              setSearchText(e.target.value);
            }}
            onKeyPress={(e) => {
              if (e.key == "Enter") {
                searchContext.setSearch(SearchText);
              }
            }}
          />
          <button
            type="button"
            onClick={(e) => {
              searchContext.setSearch(SearchText);
            }}
          >
          </button>
        </div>
    </header>
  );
};

export default Header;

I have purposely simplified the above code by removing most of the code not related to the context for easier understanding. The header.jsx component in my website got logo, menu items and dark model toggle button etc. As you can see in the above code we use useContext to access the shared context through the provider.

This way it can be accessed in any other child components where the search value is required. Also you can monitor the value of the search value to refresh the component when user enter something in search and click on search. In my products.tsx file I have added the following code as I want to search the list of products again from server.

//File Name:products.tsx

import React, { useContext, useEffect } from 'react';
import SearchContext from '../component/SearchContext';


const Products = () => {
    const { search, setSearch } = useContext(SearchContext);
    const loadProduct = () => {
        //load product code
    }

    useEffect(() => {
        loadProduct();
        return () => {
            //cleanup code
        }
    }, [search]);

    return (
        <div>
            <!-- Product code -->
        </div>
    );
};


export default Products;

I hope the above code is easier to understand. As always I have removed the code which are not relevant for the understanding of the focused part. In the above code we listen for the changes in search and calls loadProduct function as soon as the value in search changes.

I hope have explained it well. If you have got any questions related to useContext feel free to comment and I am happy to answer them or update this post to cover the unanswered areas. Happy coding..

A full stack developer learning a developing web applications since my university time for more than 20 years now and Pega Certified Lead System Architect (since 2013) with nearly 16 years experience in Pega.

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top