How to use context API in React with TypeScript in Vite

After reading this article, you will be able to use Context API in React with TypeScript in Vite

How to use context API in React with TypeScript in Vite

What is Context API?

The Context API is a feature in React, a popular JavaScript library for building user interfaces, that allows one to manage and share the state between components without passing data through props at every level of the component tree. It's a way to create a global or shared state that can be accessed by any component in one's application, making it a useful tool for managing application-wide data.

File Structure

Here's a basic file structure for a Vite project with the Context API for a React application in Vite:

my-context-app/
├── src/
│   ├── components/
│   │   ├── App.tsx
│   │   ├── ChildComponent.tsx
│   ├── context/
│   │   ├── MyContext.tsx
│   ├── main.tsx
│   ├── App.css
│   ├── index.html

In this structure:

  1. src/: This directory contains the main source code for my Vite project.

  2. components/: This directory is where I place my React components.

    • App.tsx: The root component of my application.

    • ChildComponent.tsx: A child component that consumes the context.

  3. context/: This directory is for context-related code.

    • MyContext.tsx: This file defines and exports my context using the createContext function and possibly the Provider component.
  4. main.tsx: This is the entry point for my Vite application, where I import and render the root component (App) into the DOM.

  5. App.css: My application's CSS file for styling.

index.html: The HTML template for my application.

Context API setup in MyContext.tsx

Here's a sample MyContext.tsx file in Vite:

import  React,{ createContext, useContext,ReactNode,Dispatch,SetStateAction} from 'react';

export interface GlobalContextTypes {
  target: string;
  options: string[];
  setTarget: Dispatch<SetStateAction<string>>;
  changeTarget: (name: string) => void;
}

interface AppContextProps {
  children: ReactNode;
}

const GlobalContext = createContext<GlobalContextTypes | undefined>(undefined);

export const useGlobalContext=()=>useContext(GlobalContext_;
const AppContext=React.FC<AppContextProps>=({children})=>{
    const [target,setTarget]=useState<string>("Home");
    const options:string[]=["Home","Street"];
    const changeTarget=(name:string)=>{
        setTarget(name);
    }
    return (
        <GlobalContext.Provider
            value={{
                target,
                options,
                setTarget,
                changeTarget,
            }}
        >
            {children}
        </GlobalContext.Provider>
    );
};
export default AppContext;

Let's break it down into simple terms:

  1. Creating a Context:

     import React, { createContext, useContext, ReactNode, Dispatch, SetStateAction } from 'react';
    
     export interface GlobalContextTypes {
       target: string;
       options: string[];
       setTarget: Dispatch<SetStateAction<string>>;
       changeTarget: (name: string) => void;
     }
    
     const GlobalContext = createContext<GlobalContextTypes | undefined>(undefined);
    
    • I import necessary dependencies from React, including createContext and useContext.

    • I define an interface GlobalContextTypes to describe the shape of the data I want to store in the context. It includes target (a string), options (an array of strings), setTarget (a function to set target), and changeTarget (a function to change the target).

    • I create a new context called GlobalContext with the specified data shape.

  2. Creating a Custom Hook:

     export const useGlobalContext = () => useContext(GlobalContext);
    
    • I create a custom hook useGlobalContext to easily access the data stored in my GlobalContext. This hook can be used in other components to get the context's values.
  3. Creating a Provider Component:

     interface AppContextProps {
       children: ReactNode;
     }
    
     const AppContext: React.FC<AppContextProps> = ({ children }) => {
       const [target, setTarget] = useState<string>("Home");
       const options: string[] = ["Home", "Street"];
    
       const changeTarget = (name: string) => {
         setTarget(name);
       };
    
       return (
         <GlobalContext.Provider
           value={{
             target,
             options,
             setTarget,
             changeTarget,
           }}
         >
           {children}
         </GlobalContext.Provider>
       );
     };
     export default AppContext;
    
    • I define a component AppContext that serves as the provider for my context. It takes a children prop, which represents the content that will be wrapped by this provider.

    • Inside AppContext, I set up the initial state for target using the useState hook, initializing it with "Home." I also define an options array and a function changeTarget that allows me to update the target.

    • I wrap them children with the GlobalContext.Provider, passing in the values I want to share with the context.

In summary, this code establishes a context called GlobalContext that holds data related to a target and its options. The context can be accessed using the useGlobalContext custom hook, and I've created a Provider component (AppContext) to set and provide the context's values to the components within its scope. This allows components in my application to share and update the target and options state using the Context API in TypeScript.

Import the Context API in main.tsx to use them in the entire project

Lets see inside main.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import AppContext from "./context.tsx";
ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <AppContext>
      <App />
    </AppContext>
  </React.StrictMode>
);

The code I provided is the entry point of a React application that integrates the Context API. Let's break down the relevant parts for beginners in simple terms:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import AppContext from "./context.tsx";
  • Import Statements:

    • I am importing necessary dependencies like React, ReactDOM, my App component, CSS styles, and AppContext.
ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <AppContext>
      <App />
    </AppContext>
  </React.StrictMode>
);
  • Rendering the Application:

    • <AppContext> is where the Context API comes into play. I am wrapping my entire application with this component, and that means any component within the <AppContext> will have access to the data provided by the context.

    • <App /> is my main application component, and it's nested within the <AppContext>. This means that the App component and any of its child components can access and use the data provided by the AppContext.

In simple terms, what I've done in this code is:

  1. Set up React application to render within an HTML element with the id "root."

  2. Wrapped my entire application with <AppContext>, which means I am providing a shared context to all components within my app.

  3. Rendered the App component within the context, allowing it and its child components to access and use the data defined in the AppContext.

This enables me to manage and share data across different parts of my application using the Context API, making it accessible where needed while keeping my component tree organized and efficient.

Let's use the Context API in ChildComponent.tsx

Let's see how I used Context API in ChildComponent.tsx:

import {useGlobalContext} from '../context/MyContext.tsx'

const ChildComponent=()=>{
 const context=useGlobalContext();
 if(!context)
 {
  return null;
 }
 const {target,options,changeTarget,}=context;
 return (
     <div>
        {options.map((e:string)=>{
            return(
             <button onCLick={()=>changeTarget=(e)}>{e}</button>
            )
        }
        <div>Selected:{target}</div>
    </div>
 );
}
export default ChildComponent;

Let's break down the relevant parts of Context API in simple terms:

import { useGlobalContext } from '../context/MyContext.tsx'
  • Import Statement:

    • I am importing the useGlobalContext hook from my MyContext.tsx file. This hook allows me to access the context data that I've defined in my context.
const ChildComponent = () => {
  const context = useGlobalContext();
  if (!context) {
    return null;
  }
  const { target, options, changeTarget } = context;
  • Using the Context:

    • Inside the ChildComponent function, I call the useGlobalContext hook to access the context data defined in MyContext.tsx. This hook essentially connects me component to the context, providing access to the data stored in the context.

    • I check if the context exists. If it doesn't, it means that the context might not be available (e.g., if the context hasn't been set up properly or if it's outside the scope of the provider). In that case, I will return null to avoid rendering anything, preventing potential errors.

return (
    <div>
      {options.map((e: string) => {
        return (
          <button onClick={() => changeTarget(e)}>{e}</button>
        )
      })}
      <div>Selected: {target}</div>
    </div>
  );
  • Rendering Components with Context Data:

    • I am rendering the component's user interface based on the context data I've accessed.

    • I use a map function to iterate over the options array from the context. For each option, I create a button. When the button is clicked, it calls the changeTarget function from the context with the e (which is the current option). This allows me to change the target value in the context when a button is clicked.

    • Finally, I display the currently selected target by accessing the target value from the context.

In simple terms, what's happening in this ChildComponent is:

  1. It uses the useGlobalContext hook to access the context data defined in MyContext.tsx.

  2. It checks if the context data is available, and if it is, it renders a list of buttons based on the options from the context.

  3. When a button is clicked, it calls the changeTarget function from the context to update the target value.

  4. It displays the currently selected target, which is part of the context data.

This is how the Context API allows me to share and manage data across different components in my application. The ChildComponent can interact with and display data from the context without needing to pass it through props, making m code more organized and efficient.

Conclusion

In this article, we've explored the Context API in React with TypeScript, specifically in the context of a Vite project. The Context API provides a powerful way to manage and share state across your components, eliminating the need to pass data through props at each level of your component tree. This approach simplifies data management and promotes a cleaner and more efficient code structure.

We've walked through the essential components of a Context API implementation, from setting up the file structure to defining the context and creating a provider component. With a basic understanding of how the Context API works, you can create a shared global state for your application and access it wherever needed.

In the example provided, we've seen how a child component, ChildComponent, effortlessly accesses and modifies the context's data, making your application more organized and scalable. This approach simplifies data sharing and management, especially in larger and more complex projects.

As you continue to explore React and build applications, the Context API, combined with TypeScript and Vite, becomes a valuable tool for keeping your codebase clean and maintainable. It allows you to efficiently share data and functionality between components, leading to a more robust and user-friendly user interface.

So, the next time you find yourself in a situation where you need to share data across multiple components, consider giving the Context API a try. It might just be the solution you need to streamline your application's state management and improve your development workflow. Happy coding!