Context API

To pass data through the component tree

In React, data is passed from top-down (parent to child) via props by drilling/passing the props at each direct child level. Context provides a way to pass data through multi-level child component tree without having to passing the props at each direct child level.

data flow in Context

The React Context API is designed to share data: - between a provider component and its descendants, - not between a provider and its ancestor.


Syntax

createContext()

returns: createContext returns a context object.

const someContextObj = createContext(defaultValue)

SomeContext.Provider

Wrap your components into a context provider to specify the value of this context for all components inside:

<someContextObj.Provider value={someData}>
   <Page />
</someContextObj.Provider>

SomeContext.Consumer

Before useContext existed, there was an older way to read context:

<someContextObj.Consumer>
   <h1>{someData}</h1>
</someContextObj.Consumer>

Although this older way still works, but newly written code should read context with useContext() instead:

useContext() Hook

useContext is a React Hook that lets you read and subscribe to context from your component.

//In Consumer component
const someData = useContext(someContextObj)

returns: useContext returns the context value for the calling component.


Usage

Example 1:

Creating context

Context lets components pass information deep down without explicitly passing props.

Call createContext outside any components to create one or more contexts.

myContext.js

import { createContext } from "react";

//createContext returns a context object. 
const myNewContext_1 = createContext("Karthi");
const myNewContext_2 = createContext("Monisha");

export { myNewContext_1, myNewContext_2 };  

Using context

A.js

import { myNewContext_1, myNewContext_2 } from "./myContext";
import { useContext } from "react";

function A() {
   //Components can read context by passing it to useContext()
   const data1 = useContext(myNewContext_1);
   const data2 = useContext(myNewContext_2);

   return (
      <div>
         <h1>{data1}</h1> //Karthi
         <p>{data2}</p> //Monisha
      </div>
   );
}

export default A; 

Example 2:

Let' say there are components A,B,C. Iam going to pass data from A to C component directly

Creating context

Context lets components pass information deep down without explicitly passing props.

Call createContext outside any components to create one or more contexts.

MyContext.js

import { createContext } from "react";

   const ThemeContext = createContext();
   const AuthContext = createContext();
   
   export { ThemeContext, AuthContext };

Provider component

A.js

import { useState } from "react";
import { ThemeContext, AuthContext } from "./MyContext";
import B from "./B.js";
import "./A.css";

function A2() {
   const [theme, setTheme] = useState("light");
   const [user, setUser] = useState({ name: "karthi" });

   return (
   <ThemeContext.Provider value={{ theme, setTheme }}>
      <AuthContext.Provider value={user}>
         <B />
      </AuthContext.Provider>
   </ThemeContext.Provider>
   );
}

export default A;   

Middle Components

B.js

import C from "./C";

function B() {
   return (
      <div>
         This is B componenet
         <C />
      </div>
   );
}

export default B;

Consumer Component

C.js

import { useContext } from "react";
import { ThemeContext, AuthContext } from "./MyContext";

function C() {
   const { theme, setTheme } = useContext(ThemeContext);
   const user = useContext(AuthContext);

   return (
      <div class={theme}>
         This is C componenet
         <p>{user.name}</p>
         <button 
            onClick={() => 
               setTheme(theme === "light" ? "dark" : "light")
            }
         >
            Change Theme
         </button>
      </div>
   );
}

export default C;

data flow - other options

If you need to pass data from a child to a parent or between siblings, you generally have a few options:

  1. Passing Callback Functions as props

    You can pass callback functions from the parent to the child components as props. The child components can then call these functions to communicate with the parent (Pass data in the function call parameter). eg: func1(data)

  2. Redux or other State Management Libraries

    If your application is complex and you need to manage global state, you might consider using state management libraries like Redux. These libraries allow you to manage application state in a centralized store that can be accessed by any component.

  3. Event Bus

    You could use an event bus or a pub/sub pattern to create a simple mechanism for components to communicate indirectly. However, this approach can lead to some challenges and is generally not recommended for complex applications.