Exploring React's useContext Hook

20. August, 2023 4 min read Teach

Simplifying State Management

In the world of React development, managing state and passing data between components can sometimes be a tricky task.

Luckily, React offers a solution in the form of the useContext hook, which provides a cleaner and more efficient way to share state across components without the need for complex property drilling.

It allows us to access the context of a parent component without needing to explicitly pass props through all intermediary components. Context is a way to share data like state, themes, or other global information among components without manually passing the data down the component tree.

Using Property Drilling

In a typical React application, we might have a component hierarchy like this:

// app.js
import React, { useState } from 'react';
import Navbar from 'components/nav';

const App = () => {
  const [activeItem, setActiveItem] = useState('Home');

  return (
    <div className="app">
      <Navbar activeItem={activeItem} setActiveItem={setActiveItem} />
      {/* other stuff */}
    </div>
  );
};
// nav.js
import React from 'react';
import NavItem from 'components/nav-item';

const Navigation = ({ activeItem, setActiveItem }) => {
  return (
    <nav>
      <NavItem
        title="Home"
        activeItem={activeItem}
        setActiveItem={setActiveItem}
      />
      <NavItem
        title="About"
        activeItem={activeItem}
        setActiveItem={setActiveItem}
      />
    </nav>
  );
};
// nav-item.js
import React from 'react';

const Item = ({ title, activeItem, setActiveItem }) => {
  const handleClick = () => setActiveItem(title);

  return (
    <div className={title === activeItem ? 'active' : ''} onClick={handleClick}>
      {title}
    </div>
  );
};

Using this approach, we’re passing the activeItem and setActiveItem props down the component tree from App to Navbar and then to individual, NavItem components. This approach can become cumbersome as our application grows, especially when we have a more complex state or a deeper component tree.

Using useContext for Simplification

Now, let’s see how using the useContext hook can simplify this process:

// app.js
import React, { useState } from 'react';
import Navbar from 'components/nav';
import { NavContextProvider } from 'components/nav-context';

const App = () => {
  return (
    <NavContextProvider>
      <div className="app">
        <Navbar />
        {/* other stuff */}
      </div>
    </NavContextProvider>
  );
};
// nav-context.js
import React, { createContext, useContext, useState } from 'react';

const NavContext = createContext();

const NavContextProvider = ({ children }) => {
  const [activeItem, setActiveItem] = useState('Home');

  return (
    <NavContext.Provider value={{ activeItem, setActiveItem }}>
      {children}
    </NavContext.Provider>
  );
};

const useNavContext = () => {
  return useContext(NavContext);
};

export { NavContextProvider, useNavContext };
// navbar.js
import React from 'react';
import NavItem from 'components/nav-item';
import { useNavContext } from 'components/nav-context';

const Navbar = () => {
  const { activeItem } = useNavContext();

  return (
    <nav>
      <NavItem title="Home" />
      <NavItem title="About" />
    </nav>
  );
};
// nav-item.js
import React from 'react';
import { useNavContext } from 'components/nav-context';

const NavItem = ({ title }) => {
  const { activeItem, setActiveItem } = useNavContext();
  const handleClick = () => setActiveItem(title);

  return (
    <div className={title === activeItem ? 'active' : ''} onClick={handleClick}>
      {title}
    </div>
  );
};

Using the useContext hook and creating a NavContext to manage the activeItem state, we’ve simplified the process of sharing data between components. We no longer need to pass props through multiple levels of the component tree; we can directly access the context where it’s needed.

Summary

The useContext hook simplifies the codebase, eliminates unnecessary props, and leads to cleaner, more maintainable code. While there are scenarios where useContext might not be the ideal choice, such as simple component hierarchies or third-party library integration, it shines when dealing with deep component trees and complex state management requirements.

Utilizing the useContext hook can streamline our React development process and create more efficient and elegant applications.

‘Till next time!