useContext in react

useContext in react

ยท

5 min read

๐Ÿ“– Prerequisites

  • Hooks in react
  • Basics of react

๐Ÿ prop drilling

Before diving into the useContext we need to know

  • why we are using the context?
  • what problem does it solve?

Suppose your component tree look like this

<GreatGrandParent>
   <GrandParent>
      <Parent>
         <Child/>
      </Parent>
   </GrandParent>
<GreatGrandParent/>

Now, the <GreatGrandParent/> has some state let's say count which you want to pass to the <Child/> component. How will you do it?

You will pass it as props like this flow <GreatGrandParent> -> <GrandParent> -> <Parent> -> <Child/>

<GreatGrandParent>
   <GrandParent count={count}>
      <Parent count={count}>
         <Child count={count}/>
      </Parent>
   </GrandParent>
<GreatGrandParent/>

Even though <GrandParent> and <Parent> doesn't require a count state at all, we are using them as the carrier to our state. This is known as prop drilling.

This is just a small example. But as the application grows it becomes a very cumbersome process to pass down the props from parent to deep down child component.

To solve this problem React introduce context for us.

๐Ÿค” what is the context?

From React doc.

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

In a typical React application, data is passed top-down (parent to child) via props, but such usage can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.

๐Ÿ™„ How to create context?

To create the context react provided us createContext() API. To use it we need to import it from 'react'.

import { createContext } from 'react';

const ThemeContext = createContext();

We can also provide default value to createContext()

const ThemeContext = createContext({ theme: 'dark' });

createContext() is composed of two elements:

  • Provider: provides the value
  • Consumer: consumes the value

๐Ÿ™„ How to provide context?

To provide the context we just wrap our parent node with Provider so that value will be available for all the children components.

This Provider takes a param value, which is the value/state we want to use in our child components;

<ThemeContext.Provider  value={{ theme: 'light' }}>
      <App />
</ThemeContext.Provider>

We can also use Provider by destructuring it from ThemeContext like this:

const { Provider, Consumer} = ThemeContext;

<Provider  value={{ theme: 'light' }}>
      <App />
</Provider>

๐Ÿ™„ How to consume context?

To use the context in react, React provided us useContext() hook.

import { useContext } from 'react';

To consume the context we need to pass the context which we want to use to useContext() hook.

const { theme } = useContext(ThemeContext);

In this example, we can use this theme value to change our stylings according to a dark or light theme

export default function App() {
  const { theme } = useContext(ThemeContext);
  return (
    <div className={`${theme}`}>
      <Nav />
      <PageLayout />
    </div>
  );
}

๐Ÿ”ฅ Extract the provider

We can extract the Provider into a separate file using the children prop in react as a good practice to keep our context logic separate.

You can read about children prop from here

Let's create the file theme-context.js

import { createContext, useContext } from "react";

const ThemeContext = createContext({ theme: "light" });

const ThemeProvider = ({ children }) => {
  return (
    <ThemeContext.Provider value={{ theme: 'light' }}>
      {children}
    </ThemeContext.Provider>
  );
};

export { ThemeProvider };

Now, we can wrap our parent component with ThemeProvider directly.

import { ThemeProvider } from "./theme-context";

<ThemeProvider>
   <App />
</ThemeProvider>

๐Ÿคฏ Create a custom hook to consume context

As we have seen to consume the context we use useContext(). But the problem is every time we want to use useContext() we have to import Context and provide it to useContext().

import { ThemeContext } from './theme-context`;

const { theme } = useContext(ThemeContext);

We can extract this logic into a custom hook. let's create useTheme() in theme-context.js

import { createContext, useContext } from "react";

const ThemeContext = createContext({ theme: "light" });

const useTheme = () => useContext(ThemeContext);

const ThemeProvider = ({ children }) => {
  return (
    <ThemeContext.Provider value={{ theme }}>
      {children}
    </ThemeContext.Provider>
  );
};

export { ThemeProvider, useTheme };

Now, whenever we want to consume context we can just call useTheme() hook.

import { useTheme } from "./theme-context";

export default function App() {
  const { theme } = useTheme();
  return (
    <div className={`${theme}`}>
      <Nav />
      <PageLayout />
    </div>
  );
}

๐Ÿง State in the context

It's not always the case that we have static value in context. we will mostly be using state in context so that we can modify the value too.

Let's create a theme state in theme-context.js and the handler changeTheme to change the theme.

import { createContext, useContext, useState } from "react";

const ThemeContext = createContext({ theme: "light" });

const useTheme = () => useContext(ThemeContext);

const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState("light");

  const changeTheme = () => {
    setTheme((theme) => (theme === "light" ? "dark" : "light"));
  };

  return (
    <ThemeContext.Provider value={{ theme, changeTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

export { ThemeProvider, useTheme };

Now we can use these states anywhere in our component Provider scope

const { changeTheme } = useTheme();
...
...
<button onClick={ changeTheme }>Change theme</button>
...

or

const { theme } = useTheme();
...
...

or both

const { theme, changeTheme } = useTheme();
...
...

That's it for this article. Thanks for reading.๐Ÿคฉ

thank you.gif

ย