React.js 04: Context API


Docs

lingo

  • "Components subscribing to state" ?


Context API

TLDR: app-wide states that you can inject anywhere


Context: How To

Contexts come with a .Provider component that can also take in a value to be made available to child components – without having to prop drill the value.

Child components, acting as Consumers, may subscribe to a Context’s value from their closest parent Provider with React’s useContext() hook.

Components subscribing to a Context will receive the value for the Provider closest to them in the application tree.

Providers may be given an object containing the React state and its corresponding state updater function. Subscribing child components may then use the state updater function to update the state for the Context.

Three parts:

  • Context providers

  • Context wrappers

  • useContext Hook


Context: Example 1: Without Context API

 // App.js
 import React from "react";
 import ReactDOM from "react-dom/client";

 import { ContactItem } from "./ContactItem";

 const family = [
  { name: "Finn the Human" },
  { name: "Jake the Dog" },
  { name: "Dan the Great" },
 ];

 function App() {
  return (
  	  <>
       <div>
        <h1>Contact Item 1</h1>
        <ContactItem name={family[0].name} theme="dark"/>
       </div>
       <div>
        <h1>Contact Item 2</h1>
        <ContactItem name={family[1].name} theme="light"/>
       </div>
      </>
  );
 }
 export default App;

 // ContactItem.js
 import React from "react";

 export const ContactItem = ({ name , theme }) => {
  return (
    <div className={`theme-${theme}`}>
      {name} ! <br/><br/>
      Prop theme is {theme}  <br/>
    </div>
  );
 };

Context: Example 1: With Context API

 // ThemeContext.js
 import React from 'react';

 export const ThemeContext = React.createContext();

 // App.js
 import React from "react";
 import ReactDOM from "react-dom/client";
 import {ThemeContext} from "./ThemeContext";

 import { ContactItem } from "./ContactItem";

 const family = [
  { name: "Finn the Human" },
  { name: "Jake the Dog" },
  { name: "Dan the Great" },
 ];

 function App() {
  return (
  	<>
     <ThemeContext.Provider value={"light"}>
      <div>
        <h1>Contact Item</h1>
        <ContactItem name={family[0].name}/>
      </div>
     </ThemeContext.Provider>
     <ThemeContext.Provider value={"dark"}>
      <div>
        <h1>Contact Item 2</h1>
        <ContactItem name={family[1].name}/>
      </div>
     </ThemeContext.Provider>
    </>
  );
 }
 export default App;

 // ContactsItem.js
 import React , {useContext} from "react";
 import {ThemeContext} from "./ThemeContext";

 export const ContactItem = ({ name }) => {
  const themeInContext = useContext(ThemeContext);
  return (
    <div className={`theme-${themeInContext}`}>
      {name} ! <br/><br/>
      Context theme is {themeInContext} {themeInContext === "dark" ? "🌑" : "☀"}
    </div>
  );
 };

Context: Example 1: With Context API + Wrapper

 // ThemeContext.js
 import React , {useState} from 'react';

 export const ThemeContext = React.createContext();

 export const ThemeArea = ({children,initialTheme}) => {
  const [count, setCount] = useState(0);
  return (
    <ThemeContext.Provider value={{initialTheme,count,setCount}}>
      {children}
    </ThemeContext.Provider>
  );
 };

 // App.js
 import React from "react";
 import {ThemeArea} from "./ThemeContext";

 import { ContactItem } from "./ContactItem";

 const family = [
  { name: "Finn the Human" },
  { name: "Jake the Dog" },
  { name: "Dan the Great" },
 ];

 function App() {
  return (
    <>
    <ThemeArea initialTheme={"light"}>
      <div>
        <h1>Contact Item 1</h1>
        <ContactItem name={family[0].name}/>
      </div>
    </ThemeArea>
    <ThemeArea initialTheme={"dark"}>
      <div>
        <h1>Contact Item 2</h1>
        <ContactItem name={family[1].name}/>
      </div>
    </ThemeArea>
    </>
  );
 }
 export default App;


 // ContactItem.js
 import React , {useContext} from "react";
 import {ThemeContext} from "./ThemeContext";

 export const ContactItem = ({ name }) => {
  const { initialTheme, count, setCount } = useContext(ThemeContext);
  return (
    <div className={`theme-${initialTheme}`}>
      {name} ! <br/><br/>
      Context theme is {initialTheme} {initialTheme === "dark" ? "🌑" : "☀"} <br/><br/>
      <button onClick={() => setCount(count => count+1)}>
        Clicked: {count} time(s)
      </button>
    </div>
  );
 };

Context: Example 1: With Context API + Wrapper + 2 Buttons

ThemeContext.js

 import React , {useState} from 'react';

 export const ThemeContext = React.createContext();

 export const ThemeArea = ({children,initialTheme}) => {
  const [count, setCount] = useState(0);
  const [fontSize, toggleFontSize] = useState("large");
  return (
    <ThemeContext.Provider value={{initialTheme,count,setCount,fontSize,toggleFontSize}}>
      {children}
    </ThemeContext.Provider>
  );
 };

App.js

 import React from "react";
 import {ThemeArea} from "./ThemeContext";

 import { ContactItem } from "./ContactItem";

 const family = [
  { name: "Finn the Human" },
  { name: "Jake the Dog" },
  { name: "Dan the Great" },
 ];

 function App() {
  return (
    <>
    <ThemeArea initialTheme={"light"}>
      <div>
        <h1>Contact Item 1</h1>
        <ContactItem name={family[0].name}/>
      </div>
    </ThemeArea>
    <ThemeArea initialTheme={"dark"}>
      <div>
        <h1>Contact Item 2</h1>
        <ContactItem name={family[1].name}/>
      </div>
    </ThemeArea>
    </>
  );
 }
 export default App;

ContactItem.js

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

 export const ContactItem = ({ name }) => {
  const { initialTheme, count, setCount, fontSize, toggleFontSize } = useContext(ThemeContext);
  return (
    <div className={`theme-${initialTheme}`}>
      {name} ! <br/><br/>
      Context theme is {initialTheme} {initialTheme === "dark" ? "🌑" : "☀"} <br/><br/>
      <button onClick={() => setCount(count => count+1)}>
        Clicked: {count} time(s)
      </button><br/><br/>
      <button onClick={() => toggleFontSize(toggleFontSize => (fontSize==="large") ? "regular" : "large")}>
        Click To Switch Font Size
      </button>
      now it's "{fontSize}"
    </div>
  );
 };

Context: Example 3

App.js

 import React from 'react';
 import ReactDOM from 'react-dom/client';
 import { ContactsSection } from "./ContactsSection";
 import { ThemeArea } from "./ThemeContext";

 const family = [{name: "Finn the Human"},{name: "Jake the Dog"}];
 const friends = [{name: "Marceline"},{name: "Princess Bubblegum"}];
 function App() {
  return (
    <div>
      <h1>Contacts</h1>
      // JXL tag for the context
      <ThemeArea initialTheme="light">
        <ContactsSection contacts={family} name="Family" />
      </ThemeArea>
      <ThemeArea initialTheme="dark">
        <ContactsSection contacts={friends} name="Friends" />
      </ThemeArea>
    </div>
  );
 }

ThemeContext.js

 import React , { useState } from "react";

 // to get the props
 export const ThemeContext = React.createContext();

 // defines JXL tag to pass the prop
 export const ThemeArea = ({ children, initialTheme }) => {
  const [theme,setTheme] = useState(initialTheme);
  
  return (
    <ThemeContext.Provider value={{theme:theme,setTheme:setTheme}}>
      {children}
    </ThemeContext.Provider>
  )
 }

ContactsSection.js

 import React from "react";
 import { ContactsList } from "./ContactsList";

 import { ThemeContext } from "./ThemeContext";

 export const ContactsSection = ({ contacts, name }) => {
  const {theme} = useContext(ThemeContext);  // get the props
  
  return (
    <div className={`theme-${theme}`}>
      <h2>{name}</h2>
      <ContactsList contacts={contacts} />
    </div>
  );
 };

Last updated