DEV Community

Amin
Amin

Posted on

How to share data between components using Contexts in React

You want to share data between sibling components? You want to send information between a complex hierarchy of parent & child components? You want to be able to update states from child to parent?

Then this article is for you. Follow the steps to know how to create powerful contexts in React to help share data between components easily with an example focused on a user email shared between two sibling components.

Setup

This example is based on the Vite bundler.

npm install --save-dev --save-exact vite
npm install --save --save-exact react react-dom react-router-dom
Enter fullscreen mode Exit fullscreen mode

HTML entry point

This will serve as the entry point of our website.

touch index.html
Enter fullscreen mode Exit fullscreen mode
<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="React">
    <title>React</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="./index.jsx" type="module"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

JSX entry point

This is the file that is run when hitting the website.

touch index.jsx
Enter fullscreen mode Exit fullscreen mode
import React from "react";
import {createRoot} from "react-dom/client";
import {App} from "./components/app";

createRoot(document.getElementById("root")).render(
  <App />
);
Enter fullscreen mode Exit fullscreen mode

User context

This context is the object that will hold and share our data.

mkdir contexts
touch contexts/user.js
Enter fullscreen mode Exit fullscreen mode
import {createContext} from "react";

export const UserContext = createContext();
Enter fullscreen mode Exit fullscreen mode

User custom hook

We will create a hook to ease the use of the user context.

mkdir hooks
touch hooks/user.js
Enter fullscreen mode Exit fullscreen mode
import {useContext} from "react";
import {UserContext} from "../contexts/user";

export const useUser = () => useContext(UserContext);
Enter fullscreen mode Exit fullscreen mode

User provider

The provider will help us share the user's data will all the components that we allow access to this data.

mkdir providers
touch providers/user.jsx
Enter fullscreen mode Exit fullscreen mode
import React, {useReducer, useMemo, useCallback} from "react";
import {UserContext} from "../contexts/user";

const initialState = {
  email: "johndoe@domain.com"
};

const reducer = (state, action) => {
  switch (action.type) {
    case "UPDATE_EMAIL":
      return {
        ...state,
        email: action.payload
      };

    default:
      return state;
  }
};

export const UserProvider = ({children}) => {
  const [user, dispatch] = useReducer(reducer, initialState);

  const updateEmail = useCallback(event => {
    dispatch({
      type: "UPDATE_EMAIL",
      payload: event.target.value
    });
  });

  const value = useMemo(() => {
    return {user, updateEmail};
  }, [user, updateEmail]);

  return (
    <UserContext.Provider value={value}>
      {children}
    </UserContext.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

User details page

This is a simple page that will illustrate how to read data from our context.

mkdir pages
touch pages/user-details.jsx
Enter fullscreen mode Exit fullscreen mode
import React from "react";
import {useUser} from "../hooks/user";

export const UserDetails = () => {
  const {user} = useUser();

  return (
    <>
      <h1>User details</h1>
      <p>Email: {user.email}</p>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

User form page

This is yet another simple page that will illustrate how to read and update date from & to our context.

touch pages/user-form.jsx
Enter fullscreen mode Exit fullscreen mode
import React, {useCallback} from "react";
import {useUser} from "../hooks/user";

export const UserForm = () => {
  const {user, updateEmail} = useUser();

  return (
    <div>
      <label htmlFor="email">Email</label>
      <input
        id="email"
        type="email"
        value={user.email}
        onChange={updateEmail} />
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Application component

This is the component that will hold the logic of our application.

mkdir components
touch components/app.jsx
Enter fullscreen mode Exit fullscreen mode
import React from "react";
import {BrowserRouter, Routes, Route, Link} from "react-router-dom";
import {UserProvider} from "../providers/user";
import {UserDetails} from "../pages/user-details";
import {UserForm} from "../pages/user-form";

export const App = () => {
  return (
    <BrowserRouter>
      <UserProvider>
        <header>
          <ul>
            <li>
              <Link to="/user/details">
                User details
              </Link>
            </li>
            <li>
              <Link to="/user/form">
                User form
              </Link>
            </li>
          </ul>
        </header>
        <main>
          <Routes>
            <Route
              path="/user/details"
              element={<UserDetails />} />
            <Route
              path="/user/form"
              element={<UserForm />} />
          </Routes>
        </main>
      </UserProvider>
    </BrowserRouter>
  );
};
Enter fullscreen mode Exit fullscreen mode

Start

This will start a development server so that we can see the result of our application.

npx vite
Enter fullscreen mode Exit fullscreen mode

Top comments (0)