Scotch.io's Real-World Vue Book is launching! Get 50% Off

10 React Challenges (Beginner)

Use Context to Pass Data

Data in React applications is usually passed to components, via props. By using props, data is sent from parent to child components.

Passing data to deeply nested child components can be cumbersome, especially when dealing with child components several levels down a component tree. Without React Context, we resort to a technique called "prop drilling" where we pass data down components even though some of those components don't need that specific data.

React Context provides the ability to create data and pass this data via a provider to any component in a React application.

In this React challenge, you are required to create a Context in a react application, retrieve and utilize the data in several separate components as well as update the data in context.

Context in React allows you to pass data to any component without "prop drilling".

The Challenge: Use React Context

You are required to create user data containing name and location in context, pass this data to two individual components, and update the data from a separate component.

The requirements for this challenge are:

  • Create a React context with name and location data
  • Wrap parent component with context.
  • Retrieve data in differ components.
  • Update data in a different component.

Starter CodeSandbox

Fork the CodeSandbox to get started: https://codesandbox.io/s/challenge-9-use-react-context-starter-s8smj

There are provided styles in styles.css, feel free to use them.

Here's what the final page looks like:

Hint

Lookup Context in React applications. This post from the React docs is super helpful.

Fork this CodeSandbox to get started: https://codesandbox.io/s/challenge-9-use-react-context-final-qq2bg

Solution: Use React Context

React Context allows us to pass and update data across components of a React application. You can find more about React context here.

In this challenge, we are required to create data of a user's name and location in context, pass it to two different components, and update the value of this data from an entirely different component.

None of these components possesses a parent-child relationship.

This challenge is completed in 4 steps:

  • Create the User context.
  • Wrap the parent <App/> component with the context provider.
  • Retrieve Name and Location data in the Name and Location components, respectively.
  • Update data stored in Context.

Create User Context

WIth Context in React, we create a global variable which is accessible throughout the application. The context provider is used to wrap a parent component, and each child anywhere in the application can subscribe to the data in the provider and any subsequent updates.

In the root directory of the application, create UserContext.js to hold the context instance as well as create the context variables.

In UserContext.js, create the context object using the createContext API. Set up the context data with:

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

export const UserContext = createContext();

// This context provider is passed to any component requiring the context
export const UserProvider = ({ children }) => {
  const [name, setName] = useState("William");
  const [location, setLocation] = useState("Mars");

  return (
    <UserContext.Provider
      value={{
        name,
        location,
        setName,
        setLocation
      }}
    >
      {children}
    </UserContext.Provider>
  );
};

Here, we exported UserContext which can be consumed by any child component of UserProvider. The useState React hook was used to hold the state variables of name and location, with their corresponding methods to update them.

These data are passed via the value in the provider to any subscriber of the provider.

Wrap the App component with Context.

In index.js import UserProvider from the context file and wrap the <App/> component with it. This way the context data is available throughout the application. Do this with:

import React from 'react';
import ReactDOM from 'react-dom';
import { UserProvider } from './UserContext';
import // ... import dependencies

// This component updates with data from context
function App() {
  return (
    <div className="App">
      // Component definition goes in here
    </div>
  );
}

const rootElement = document.getElementById('root');
ReactDOM.render(
  // wrap root element with context
  <UserProvider>
    <App />
  </UserProvider>,
  rootElement
);

All context data is currently provided to the root element and can be subscribed to by its children.

Use Name and Location data in components

In NameComponent.js import UserContext and using the useContext hook, subscribe to the context data. Update the name displayed to use the data from context. Do this with:

import React, { useContext } from 'react';
import { UserContext } from './UserContext';

// This component displays name from Context
const Name = () => {
  const user = useContext(UserContext);

  return (
    <div style={{ marginTop: '30px' }}>
      <h2 className="is-size-4">
        <strong>Name</strong>: {user.name}
      </h2>
    </div>
  );
};

export default Name;

With this, the name is pulled from corresponding data in context.

Do the same for location data in LocationComponent.js with:

import React, { useContext } from 'react';
import { UserContext } from './UserContext';

// This component displays location from context
const Location = () => {
  const user = useContext(UserContext);

  return (
    <div>
      {/_ Display user's location from Context _/}
      <h2 className="is-size-4">
        <strong>Location</strong>: {user.location}
      </h2>
    </div>
  );
};

export default Location;

Next up, we will update the context data from a separate component, and each component subscribed to it receives the update.

Update Data in Context

In UserForm.js, import UserContext, and using the useContext hook, consume the context data. With the setName and setLocation methods retrieved from context, call these methods on value change in their respective input forms to update the data. Do this with:

import React, { useContext } from 'react';
import { UserContext } from './UserContext';

const Form = () => {
    // Retrieve context data
  const user = useContext(UserContext);

  return (
    <div className="user-form">

            {/_ Change user's name in context _/}
      <div className="input-item">
        <label className="label">Update Name: </label>
        <input
          className="input"
          onChange={e => user.setName(e.target.value)}
        />
      </div>

      {/_ Change user's location in context _/}
      <div className="input-item">
        <label className="label">Update Location: </label>
        <input
          className="input"
          onChange={e => user.setLocation(e.target.value)}
        />
      </div>
    </div>
  );
};

export default Form;

With this, once a change is detected in a specific input, it fires the corresponding method, which changes the value in context. This updates all the other component subscribed to the context.

Here's what the final page looks like:

You can find the completed CodeSandbox here: https://codesandbox.io/s/challenge-9-use-react-context-final-qq2bg

Conclusion

In this challenge, we used React Context to pass data to components without the use of props.

Have you got questions, suggestions, and comments? We'd love to see them in the comment section. You can find other challenges here and join the live streams here. Happy keyboard slapping!

Like this article? Follow @chrisoncode on Twitter