Context API in React: Your Secret Weapon for Global State

Context API in React: Your Secret Weapon for Global State

The Context API is a part of React, a popular JavaScript library for building user interfaces, that provides a way to manage and share state and data across components in an efficient and organized manner.

It helps solve the problem of “prop drilling” which is the process of passing props (properties) through multiple levels of nested components just to get data from a top-level component to a deeply nested one. Prop drilling can make your codebase harder to maintain and less readable as your application grows in complexity.

Prop drilling

Rules for Context API

  • create Context() → creation

  • provider → Context provider

  • consume

Steps

  • Create a “context” folder in the src folder then create an AppContext.js file

Here we take a small part of a project to understand the concepts of context API

  • Create Context

import { createContext} from "react";
//step1
export const AppContext = createContext();
  • Create Provider

function AppContextProvider({children}) {
   // after some time we will see
}

Then go to the index.js file the wrap the <App /> component with <AppContextProvider />

  <AppContextProvider>
    <App />
  </AppContextProvider>

Now we have to create state variables that are passed to their children components. Let’s take an example, a website that used some data like posts, pageNumber, loading, and totalPage in the whole website. Then we have to create the state variables for these data so we can use these data anywhere without “prop drilling”.

import { createContext, useState } from "react";

//step1
export const AppContext = createContext();

export default function AppContextProvider({children}) {
    // NEW CODE
    // create state variables which are used in context api
    const [loading, setLoading] = useState(false);
    const [posts, setPosts] = useState([]);
    const [page, setPage] = useState(1);
    const [totalPages, setTotalPages] = useState(null);
}

Now we have to create a value that which data are used in any children of AppContext.

import { createContext, useState } from "react";

//step1
export const AppContext = createContext();

export default function AppContextProvider({children}) {

    // create state variables which are used in context api
    const [loading, setLoading] = useState(false);
    const [posts, setPosts] = useState([]);
    const [page, setPage] = useState(1);
    const [totalPages, setTotalPages] = useState(null);

        // new code
        const value = {
          posts,
          setPosts,
          loading,
          setLoading,
          page,
          setPage,
          totalPages,
          setTotalPages,
    };
}

Main things that, how to pass this value in children component.

import { createContext, useState } from "react";

//step1
export const AppContext = createContext();

export default function AppContextProvider({children}) {

    // create state variables which are used in context api
    const [loading, setLoading] = useState(false);
    const [posts, setPosts] = useState([]);
    const [page, setPage] = useState(1);
    const [totalPages, setTotalPages] = useState(null);


        const value = {
          posts,
          setPosts,
          loading,
          setLoading,
          page,
          setPage,
          totalPages,
          setTotalPages,
    };

  // new code
  //step2
    return <AppContext.Provider value={value}>
        {children}
    </AppContext.Provider>;
}

From the above part, we miss one thing which is “Data filling”. Here we use a api to get the data. So we use fetch() function to fetch the data from the API. The data looks like this:

{
  "page": 1,
  "pageSize": 5,
  "totalPosts": 30,
  "totalPages": 6,
  "posts": [
    {...},
    {...},
    {...},
    {...},
    {...}
  ]
}

Now create an async function to fill the data fetchBlogPosts

import { createContext, useState } from "react";
import { baseUrl } from "../baseUrl";
// baseUrl is a simple file where the link of api is present.

//step1
export const AppContext = createContext();

export default function AppContextProvider({children}) {

    // create state variables which are used in context api
    const [loading, setLoading] = useState(false);
    const [posts, setPosts] = useState([]);
    const [page, setPage] = useState(1);
    const [totalPages, setTotalPages] = useState(null);

    // new code
    // filling data
    async function fetchBlogPosts(page = 1) {
        setLoading(true); // 1st we set true in loading 
        let url = `${baseUrl}?page=${page}`;
        console.log("printing the final URL");
        console.log(url);
        try{
            const result = await fetch(url); // fetch the data from api
            const data = await result.json(); // convert the data into json 
            console.log(data);
            setPage(data.page); // set the page
            setPosts(data.posts); // set the post
            setTotalPages(data.totalPages) // set the total post
        }
        // handling error part
        catch(error) {
            console.log("Error in fetching data");
            setPage(1);
            setPosts([]);
            setTotalPages(null);
        }
        // after completing fetching the data then we set false to loading
        setLoading(false);
    }

        const value = {
          posts,
          setPosts,
          loading,
          setLoading,
          page,
          setPage,
          totalPages,
          setTotalPages,
          fetchBlogPosts // send the function also
    };

  //step2
    return <AppContext.Provider value={value}>
        {children}
    </AppContext.Provider>;
}

This part is extra :

Create a function to handle the page change

import { createContext, useState } from "react";
import { baseUrl } from "../baseUrl";
// baseUrl is a simple file where the link of api is present.

//step1
export const AppContext = createContext();

export default function AppContextProvider({children}) {

    // create state variables which are used in context api
    const [loading, setLoading] = useState(false);
    const [posts, setPosts] = useState([]);
    const [page, setPage] = useState(1);
    const [totalPages, setTotalPages] = useState(null);


    // filling data
    async function fetchBlogPosts(page = 1) {
        setLoading(true); // 1st we set true in loading 
        let url = `${baseUrl}?page=${page}`;
        console.log("printing the final URL");
        console.log(url);
        try{
            const result = await fetch(url); // fetch the data from api
            const data = await result.json(); // convert the data into json 
            console.log(data);
            setPage(data.page); // set the page
            setPosts(data.posts); // set the post
            setTotalPages(data.totalPages) // set the total post
        }
        // handling error part
        catch(error) {
            console.log("Error in fetching data");
            setPage(1);
            setPosts([]);
            setTotalPages(null);
        }
        // after completing fetching the data then we set false to loading
        setLoading(false);
    }

      // new code
       function handlePageChange(page) {
        setPage(page);
        fetchBlogPosts(page);
      }

        const value = {
          posts,
          setPosts,
          loading,
          setLoading,
          page,
          setPage,
          totalPages,
          setTotalPages,
          fetchBlogPosts, // send the function also
          handlePageChange
    };

  //step2
    return <AppContext.Provider value={value}>
        {children}
    </AppContext.Provider>;
}

We created centralized data using Context Provider, so we don’t have to think about How to lift the data or How to drill the data.

We completed two main steps: creation and provider.

Now we have to think “How to consume the data ?”, so the answer is “ HOOK” named “useContext”

  • Consume Data

    This part is very simple, no need to worry about where the data is present because we centralized the data. Here we use useContext() hook to consume the data. If a component is used loading data and post data then we use this hook inside that component.

      import React, { useContext } from 'react'
      import { AppContext } from '../context/AppContext'
    
      const Blogs = () => {
          const {posts,loading} = useContext(AppContext);
          console.log("Printing inside blogs component");
          console.log(posts);
      }
    

    We provide the name of the context in useContext() like useContext(AppContext).

  • Conclusion

    In summary, the Context API in React is used to manage and share global state and data between components efficiently, making your codebase more maintainable, readable, and performant. It simplifies component composition and reduces the complexity of passing props through multiple layers of components, which can be especially beneficial in larger and more complex applications.

    Thank you for joining us on this journey through the world of React’s Context API. I hope this guide has been a valuable resource, and I look forward to seeing the amazing applications you create using this essential tool.

Special Thanks to Love Babbar bhaiya 🙏🏻

Happy coding, and keep building!