Using React Context
Tags: react, javascript
Published: 2020-05-29 06:04:34

> **This was originally posted on my [blog](https://akhilaariyachandra.com/using-react-context/ "Using React Context") on May 7th, 2020. [Check it out](https://akhilaariyachandra.com/blog/ "Blog | Akhila Ariyachandra") for my posts on React and JavaScript.**

While developing a React app you might come across a situation where you need to share a value or state between components. If the state needs to be shared between two components and they have a direct parent-child relationship, we can pass the state from the parent to the child through props. Or if we want to share the state within multiple components we might look into something like Redux.

> If you want to learn how to setup Redux in a React App, check out my [post here](https://akhilaariyachandra.com/setup-redux-in-a-react-app/ "Setup Redux in a React app"). If you want to use Redux with hooks check out my [post here](https://akhilaariyachandra.com/use-hooks-in-react-redux/ "Use Hooks in React Redux").

If the state to be shared isn't complex, Redux might be overkill as it takes some effort to setup and use. For those cases we can use React Context.

# What we will be building

![Application Preview](https://dev-to-uploads.s3.amazonaws.com/i/tktsafjesyrgl1mccorm.png)

We will build an app that fetches and displays users from [JSONPlaceholder](https://jsonplaceholder.typicode.com/).

The app will be divided into three parts.

- **Context** - Used to share the state within the app.
- **Controls** - The component used to change the user.
- **Display** - The component used to display the User data.

## The Context

The Context will share any value given to it to its direct descendants.

In our case, we will need to share four sets of data.

- `userId` - The state that holds the current User ID.
- `setUserId` - The function that updates the `userId` state.
- `user` - The state the holds the User data.
- `isFetching` - The state that will be used to indicate if the app is currently in the middle of fetching a user so that the controls can be disabled.

To start, create the `context` folder and in it create the `UserContext.js` file.

```jsx
import React from "react";
```

Next let's create and export the context with some default values.

```jsx
export const UserContext = React.createContext({
  userId: 1,
  setUserId: null,
  user: null,
  isFetching: false,
});
```

After that we'll declare a Context Provider which will expose the context to its child components.

```jsx
export const UserProvider = ({ children }) => {
  return <UserContext.Provider>{children}</UserContext.Provider>;
};
```

Then let's declare the `userId`, `user` and `isFetching` states and pass them to the provider.

```jsx
export const UserProvider = ({ children }) => {
  const [userId, setUserId] = React.useState(1);
  const [user, setUser] = React.useState(null);
  const [isFetching, setIsFetching] = React.useState(false);

  return (
    <UserContext.Provider value={{ userId, setUserId, user, isFetching }}>
      {children}
    </UserContext.Provider>
  );
};
```

Now we'll setup an [effect](https://reactjs.org/docs/hooks-effect.html "useEffect") to automatically update the `user` state whenever the `userId` state is changed.

```jsx
const fetchUser = async () => {
  try {
    setIsFetching(true);

    const response = await fetch(
      `https://jsonplaceholder.typicode.com/users/${userId}`
    );
    const responseJson = await response.json();

    setUser(responseJson);
  } catch (error) {
    console.error("> Error fetching user: ", error);
  } finally {
    setIsFetching(false);
  }
};

React.useEffect(() => {
  fetchUser();
}, [userId]);
```

Finally the `UserContext.js` file should look like this.

```jsx
import React from "react";

export const UserContext = React.createContext({
  userId: 1,
  setUserId: null,
  user: null,
  isFetching: false,
});

export const UserProvider = ({ children }) => {
  const [userId, setUserId] = React.useState(1);
  const [user, setUser] = React.useState(null);
  const [isFetching, setIsFetching] = React.useState(false);

  const fetchUser = async () => {
    try {
      setIsFetching(true);

      const response = await fetch(
        `https://jsonplaceholder.typicode.com/users/${userId}`
      );
      const responseJson = await response.json();

      setUser(responseJson);
    } catch (error) {
      console.error("> Error fetching user: ", error);
    } finally {
      setIsFetching(false);
    }
  };

  React.useEffect(() => {
    fetchUser();
  }, [userId]);

  return (
    <UserContext.Provider value={{ userId, setUserId, user, isFetching }}>
      {children}
    </UserContext.Provider>
  );
};
```

## The Display component

Next let's create the `Display` component. Create the `components` folder and in it add the file `Display.js`.

```jsx
import React from "react";

const Display = () => {
  return <div></div>;
};

export default Display;
```

Now we can get the `user` state by from the `UserContext` with the `useContext` [hook](https://reactjs.org/docs/hooks-reference.html#usecontext).

```jsx
import { UserContext } from "../context/UserContext";

const Display = () => {
  const { user } = React.useContext(UserContext);

  return <div></div>;
};
```

To finish off the `Display` component, let's display the User data in a table.

```jsx
return (
  <div>
    <table>
      <tbody>
        <tr>
          <td>ID: </td>
          <td>{user?.id}</td>
        </tr>

        <tr>
          <td>Name: </td>
          <td>{user?.name}</td>
        </tr>

        <tr>
          <td>Username: </td>
          <td>{user?.username}</td>
        </tr>

        <tr>
          <td>Email: </td>
          <td>{user?.email}</td>
        </tr>
      </tbody>
    </table>
  </div>
);
```

In the end the `Display` component should look like this.

```jsx
import React from "react";
import { UserContext } from "../context/UserContext";

const Display = () => {
  const { user } = React.useContext(UserContext);

  return (
    <div>
      <table>
        <tbody>
          <tr>
            <td>ID: </td>
            <td>{user?.id}</td>
          </tr>

          <tr>
            <td>Name: </td>
            <td>{user?.name}</td>
          </tr>

          <tr>
            <td>Username: </td>
            <td>{user?.username}</td>
          </tr>

          <tr>
            <td>Email: </td>
            <td>{user?.email}</td>
          </tr>
        </tbody>
      </table>
    </div>
  );
};

export default Display;
```

## The Controls component

The `Controls` component is used to change the current `userId`.

To start create the `Controls.js` file in the `components` folder.

```jsx
import React from "react";

const Controls = () => {
  return <div></div>;
};

export default Controls;
```

After that, we can get `userId`, `setUserId` and `isFetching` from `UserContext`.

```jsx
import { UserContext } from "../context/UserContext";

const Controls = () => {
  const { userId, setUserId, isFetching } = React.useContext(UserContext);

  return <div></div>;
};
```

Next we can add two buttons to change the `userId`.

```jsx
return (
  <div>
    <button onClick={() => setUserId(userId - 1)}>previous</button>

    <button onClick={() => setUserId(userId + 1)}>next</button>
  </div>
);
```

Finally we will add a check to the buttons to disable them if the app is already fetching a user or to stop `userId` from being set to value less than 1 or more than 10.

```jsx
return (
  <div>
    <button
      onClick={() => setUserId(userId - 1)}
      disabled={userId <= 1 || isFetching}
    >
      previous
    </button>

    <button
      onClick={() => setUserId(userId + 1)}
      disabled={userId >= 10 || isFetching}
    >
      next
    </button>
  </div>
);
```

Finally `Controls.js` file should look like this.

```jsx
import React from "react";
import { UserContext } from "../context/UserContext";

const Controls = () => {
  const { userId, setUserId, isFetching } = React.useContext(UserContext);

  return (
    <div>
      <button
        onClick={() => setUserId(userId - 1)}
        disabled={userId <= 1 || isFetching}
      >
        previous
      </button>

      <button
        onClick={() => setUserId(userId + 1)}
        disabled={userId >= 10 || isFetching}
      >
        next
      </button>
    </div>
  );
};

export default Controls;
```

# Bringing it all together

Now all that's left to is to bring everything together in the root component.

```jsx
import React from "react";

const App = () => {
  return <div className="App"></div>;
};

export default App;
```

Then we should wrap the root `div` in the `UserProvider` to make the context available to all components.

```jsx
return (
  <UserProvider>
    <div className="App"></div>
  </UserProvider>
);
```

Finally add the `Display` and `Controls` components.

```jsx
return (
  <UserProvider>
    <div className="App">
      <Display />

      <Controls />
    </div>
  </UserProvider>
);
```

In the end the root component should look like this.

```jsx
import React from "react";
import Display from "./components/Display";
import Controls from "./components/Controls";
import { UserProvider } from "./context/UserContext";

const App = () => {
  return (
    <UserProvider>
      <div className="App">
        <Display />

        <Controls />
      </div>
    </UserProvider>
  );
};

export default App;
```

# Wrapping up

Below is a sample of the app we just built. If you think you missed something, feel free to check out the code.

{% codesandbox react-context-example-le3pc %}

If you found this post helpful please be sure to share it! 😊