A practical example of the React useContext hook

The React useContext hook is an extremely useful tool that is often misused or misunderstood. This can result in components that are tricky to reuse. And that can be a real problem. So here's a quick tip on how to utilise React useContext to simplify your code and make it more reusable.

an image with an azure and yellow illustration and the caption: "A practical example of the useContext hook"

It's likely that at some point in your React app development you've had to pass down data to a large amount of components. Doing this via props can be messy and repetitive, so it becomes a perfect candidate for React useContext.

It would probably look something like this:

// in your parent component
<Context.Provider value={object}>
  <Component1 />
  <Component2 />
  {/* ... more components */}
</Context.Provider>;

// in one of the child components
const object = useContext(Context);

Don't get me wrong, this is absolutely fine... But it's not very reusable. The child components won't be able to be reused outside of this one parent component which is obviously going to create more complexity.

Here's a much better solution to make your React useContext a hundred times more useful:

Breakdown of the React useContext example

If you're not familiar with React Context the example above might be a bit confusing, so let's just do a quick breakdown of its components.

The biggest change from our initial example is the creation of a third component: ThemeProvider.

import { createContext, useContext } from "react";

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

export const ThemeProvider = ({ children }) => {
  return (
    <ThemeContext.Provider value={{ color: "rgb(30, 64, 175)" }}>
      {children}
    </ThemeContext.Provider>
  );
};

This component does two things:

  • Exports a ThemeProvider Provider component
  • Exports the useTheme hook to provide children components with access to its data

This means that all of our Children components are not dependant exclusively on this component having been imported at some point as their parent.

Therefore, we can reuse them as much as we want - as long as we also wrap them inside <ThemeProvider>!

Making React useContext even more useful

The example above is not very useful on its own, but let's see how this would work in a real world scenario...

In the above example we don't just use React useContext to pass data down to our components, but we also use it to fetch whatever data those components might need.

So if you want to ensure a child component has access to some remotely fetched data, you just need to wrap them in your new component.

Even better yet:

return (
  <DataContext.Provider value={data}>
    {data ? children : <Skeleton count={2} />}
  </DataContext.Provider>
);

By checking that data isn't undefined we can render an alternative component as the data fetches - a loading spinner or a skeleton - and your loading state will be automatically handled by the parent component.

If you have any questions, just leave them in a comment below!