Telerik blogs

React Hooks aim to solve the difficulties of logic reuse by enabling us to write components that have access to features like state, context, lifecycle methods, ref, etc. In this article, we’ll be spending our focus on the useContext hook—a useful hook that allows components to access data without the need to rely on props.

When managing data between parent and child components, React gives us the ability to use something known as props to pass data down from parent to child. Props can only flow in one direction, from parent components to child components (and further down). When state changes occur on parent elements, React will re-render components that depend on those values.

Using props works well in most cases. However, when working in large applications with a large number of components in the component tree, props can become hard to maintain since props need to be declared in each and every component in the component tree.

Context, in React, can help make things easier for us in situations like this.

Context

Context in React provides a way to pass data through a component tree without the need to prop-drill (i.e., pass props down manually at every level). We’ll go through a simple example to illustrate how this can be done.

To begin, we first need to create the Context object with the createContext() function.

import React, { createContext } from "react";

const Context = createContext();

To have context available in the entire component tree, we need to wrap the parent component markup with the Context provider to have all child components subscribe to Context changes.

If we have an <App /> component that acts as the parent component of the application, using the Context provider would look something can look something like the following:

import React, { createContext } from "react";

const Context = createContext();

const App = () => {
  return <Context.Provider>/* ... */</Context.Provider>;
};

When declaring the Context provider, we’ll go ahead and specify the context value in the value prop of <Context.Provider>. This is how we’ll provide some initial data for our example.

import React, { createContext } from "react";

const Context = createContext();

const App = () => {
  return (
    <Context.Provider value={{ data: "Data from context!" }}>
      /* ... */
    </Context.Provider>
  );
};

We’ll now assume <App /> is to render a child component named <Child />.

import React, { createContext } from "react";

const Context = createContext();

const Child = () => {
  return <div>This is the child component!</div>;
};

const App = () => {
  return (
    <Context.Provider>
      <Child />
    </Context.Provider>
  );
};

We can have the <Child /> component retrieve the current context value with the help of the useContext Hook.

useContext

The useContext Hook provides function components access to the context value for a context object. It:

  • Takes the context object (i.e., value returned from React.createContext) as the one argument it accepts.
  • And returns the current context value as given by the nearest context provider.

Description of the useContext Hook: const contextValue = useContext(ContextObject). ContextValue is the current value as given by the nearest context provider. ContextObject is value returned from ReactcreateContext

With this in mind, we’ll have the <Child /> component in our example use the useContext hook to access the data property available in our application context and render its value in its markup.

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

const Context = createContext();

const Child = () => {
  const context = useContext(Context);
  return <div>{context.data}</div>;
};

const App = () => {
  return (
    <Context.Provider value={{ data: "Data from context!" }}>
      <Child />
    </Context.Provider>
  );
};

With these changes, we’ll notice the data from the parent <App /> component be rendered in the <Child /> component with the help of the Context API.

Running example of useContext with two components

I’m using the React Developer Tools Google Chrome extension to visualize the context data in the component tree.

With Context, we would notice the same behavior as we’ve seen above even if we had numerous child components within the component hierarchy tree. As an example, assume we had <Child />, <Child2 />, <Child3 />, <Child4 /> and <Child5 /> components where each child component is a parent to the other.

We’ll have the lowest-most component (<Child5 />) attempt to access and render the Context data value in its markup.

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

const Context = createContext();

const Child5 = () => {
  const context = useContext(Context);
  return <div>{context.data}</div>;
};

const Child4 = () => {
  return <Child5 />;
};

const Child3 = () => {
  return <Child4 />;
};

const Child2 = () => {
  return <Child3 />;
};

const Child = () => {
  return <Child2 />;
};

const App = () => {
  return (
    <Context.Provider value={{ data: "Data from context!" }}>
      <Child />
    </Context.Provider>
  );
};

Data from the parent <App /> component will be rendered in the <Child5 /> component without the need to prop-drill data through every component in the tree, thanks to Context!

Running example of useContext with many child components

Props vs. Context

When should one decide to use props vs. context? There are pros and cons to each approach.

With props:

  • We have a clearly defined pattern of passing data one level at a time without the need to leverage any additional API or Hooks (pro).
  • However, the pattern of passing props data one level at a time can be cumbersome if we were to have a large number of components in our component hierarchy tree (con).

With Context:

  • We have the capability to have a child component access data from a parent component many levels above without the need to have this data passed down each level (pro).
  • However, Context can make debugging difficult if bugs arise. Debugging is more likely to be difficult if we had numerous different Context objects and providers in a large-scale application (con).

Context is best used for application-wide client data. Things like theme information, locale/language preferences, user authentication information, etc. are best kept in Context since any component within an application may have the need to access this data at any time.

Props is best used when data needs to be isolated to a few set of components only.

Wrap-up

This article covers the main concept and use case with using the useContext hook. At any time we want to have client data be available within a large number of React components, we should think of leveraging the Context API and the useContext hook.


About the Author

Hassan Djirdeh

Hassan is currently a senior frontend engineer at Doordash. Prior to Doordash, Hassan worked at Instacart and Shopify, where he helped build large production applications at-scale. Hassan is also a published author and course instructor and has helped thousands of students learn in-depth fronted engineering tools like React, Vue, TypeScript and GraphQL. Hassan’s non-work interests range widely and, when not in front of a computer screen, you can find him at the gym, going for walks or running through the six.

Related Posts

Comments

Comments are disabled in preview mode.