React Context and Hooks: An Open Source Project to Understand How They Work
Tags: react, javascript, hooks, context
Published: 2020-07-07 22:52:10

_Intermediate level article_

There are different approaches regarding the best ways to learn something new, and one of them is by doing. I agree with that approach, as long as the basics are already clear, and you have a general mental model that gives you the right context about what you are learning.

For instance, if you are going to learn how to use _Context_ and _Hooks_ from the React API, you already need to be familiar with the following topics, otherwise you will be totally lost:

- Functional components
- React Life-cycle Events
- The concept of State and State Management in JavaScript
- The concept of a Hook
- Context and Scope JavaScript concepts
- The DOM
- JavaScript modern features

If you feel comfortable with the above topics, keep reading; otherwise, you can always come back to this later.

This time, I want to share with you my experience building a React App from the ground up using the _Context_ React object and React _Hooks_, no _Class Components_ included, just _Functional Components_.

## The Project

A simple blog with a React App in the front end that allows you to search and read blog articles (built with the _Context_ and _Hooks_ React features). The articles are retrieved from a back end application built in NodeJS, fetching the data via API calls.

You can find the [open source project here](https://github.com/anayib/react-node-blog-app).

## The Objective

My objective with this project is to create a simple web app that serves as a reference for those having trouble grasping the concepts and practical aspects of using the React _Context_ object and _hooks_ to build React Apps.

## The App Architecture

### The Front End

The front end is a React App built using _Context_, _Hooks_ and _Functional Components_.

Remember that a _Context_ object is a JavaScript object that allows you to manage the state (data) of your application. In this project, we have a _Context_ object that helps us handle the article's data fetched from the back end ([Context.js](https://github.com/anayib/react-node-blog-app/blob/master/client/Context.js)) and another _Context_ that helps us handle the articles that should be available to some components in order to be shown to the user after a search has been requested ([SearchContext.js](https://github.com/anayib/react-node-blog-app/blob/master/client/SearchContext.js)).

### The Back End

The back end is built with NodeJS and Express. Its only purpose is to make an end-point available to serve the articles data in JSON format when requested from a client, in our case, from the React App.

### The Data

For this version, I did not include any database, but I used the file system to save the articles. Why? Because the focus of this project is mainly the front end, and this approach to store data is good enough to make our NodeJS API work.

## Why Use Context and Hooks

There are pros and cons regarding the use of these React API new features. Nevertheless, here are the ones I found the most relevant during this project:

- Pros: Using _Context_ allows you to pass data to any component in your app without having to pass it manually every level down the DOM tree. For this specific project, the _Context_ feature allowed me to manage the state of the blog posts in a single component (the context provider) that could be imported in any other component, in order to give it access to the data that has been previously retrived from the back end via an API call.

- Cons: Right now, it is harder to test components that use data from the _Context_ providers when using _Jest_ than testing them the traditional way. The other aspect is that using _Hooks_ makes it "more magical" when managing the state of your application data than when you are using the tradition life cycle methods from a _Class Component_.

## React Hooks vs Traditional Life Cycle Methods

I assume you are familiar with the `componentDidMount`, `componentDidUpdate`, and the other life cycle methods of React. In brief, and being simplistic for learning purposes, some of the _Hooks_ allow you to do the same as the life cycle methods, but from within _Functional Components_, there is no need to write a _Class Component_ to initialize and handle the state of the component.

Let's see an example from the project using the _useState()_ and _useEffect_ React _Hooks_. Check the following code, including the commented code which explains what every line is written for:

```javascript
// Context.js

import React, { useState, useEffect } from "react"; // imports React, and the useState and useEffect basic hooks from react library
import axios from "axios"; // imports axios from the axios package to make the API call to the back-end

const Context = React.createContext(); // creates a Context object from the React.createContext() method. You will reference this Context object when the blog posts data fetched from the NodeJS API needs to be accessible by other components at different nesting levels.

function ContextProvider() {} // Functional component definition for a component named ContextProvider. This Functional Component will be in charged of fetching the data from the back end and handle the state (blog articles) data of the application

export { ContextProvider, Context }; // export the ContextProvider functional component, and the Context object to make them available to other modules in the React app
```

With the previous code, we have created a file _Context.js_ whose only responsability will be to give to other components access to the articles' data, which is retrieved from the back end. To do so, we need to create a new _Context_ (`const Context = React.createContext()`), and a _Functional Component_ that allows us to provide that _Context_ to other components (`function ContextProvider( ) {}`)

Now that we have the basic structure of our file to handle the articles' state using our own _Context_, let's write the code inside the _ContextProvider_ _Functional Component_, which will set the initial state and handle any changes:

```javascript
import React, { useState, useEffect } from "react";
import axios from "axios";

const Context = React.createContext();

function ContextProvider({ children }) {
  const [articles, setArticles] = useState([]); // useState() hook call, that initializes the state of the articles to an empty array

  useEffect(() => {
    // useEffect hook call which will be invoked the first time the DOM mount. it is like using componentDidMount in Class Components
    fetchArticles(); // the function that will be called as soon as the DOM mounted
  }, []);

  async function fetchArticles() {
    // the asyncronous definition of the fetchArticles function that will retrieve the articles from the NodeJS api
    try {
      const content = await axios.get("/api/tutorials"); // the API call to fetch the articles from the back end
      setArticles(content.data); // the setArticles function allows us to update the state of the component via the useState() hook
    } catch (error) {
      console.log(error);
    }
  }

  return <Context.Provider value={{ articles }}>{children}</Context.Provider>; // the returned value from the component
}

export { ContextProvider, Context };
```

Let's take a closer look at every line written above.

### The ContextProvider Component

`function ContextProvider({ children }) {...}` : This is the _Functional Component_ definition that accepts a parameter called _children_. The _children_ parameter is any _Functional Component_ that will be receiving the state being handled by this _ContextProvider_ function, and are children components of the _ContextProvider_ component. [Check out this example](https://github.com/anayib/nayib-personal-blog-/blob/d1e483972213f08c5acb49167c76c5cb7931324a/client/index.js#L9).

The curly braces included in `{children}` , may appear strange to you. This is the way the new JavaScript features allow us to [deconstruct an object or array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment). For example:

```javascript
const fullName = { firstName: "Nayib", lastName: "Abdalá" };
const { firstName, lastName } = fullName; // JS object deconstruction

console.log(firstName); // Nayib
console.log(lastName); // Abdalá
```

In brief, the `const [articles, setArticles] = useState([]);` line helped us initialize and handle the state of the articles that will be fetched from the back end. Let's see how.

### The Initialization of the App State with the useState() Hook

`const [articles, setArticles] = useState([]);`: Does this line look strange to you? It is simple. The `const` keyword allows us to declare a constant called `articles` , and one called `setArticles`. The values assigned to each of these constants are the returned values from calling the `useState()` hook, which returns an array with 2 elements, and the [deconstruct JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) feature allows us to assign each of those elements to each constant we have defined on the left side of the expression `const [articles, setArticles] = useState([]);`.

The array returned by the `useState()` hook is an array containing the current state for a given variable, and a function that updates that state and can be used at any time in your _Functional Component_ in order to update that state. In this case, we are initializing the value of `articles` to an empty array (when passing `[]` to the `useState([])` function).

You can learn more about the [useState() hook here](https://reactjs.org/docs/hooks-reference.html#usestate).

### Listening for State Changes with the useEffect() Hook

`useEffect(() => { ... }, [])`:

The `useEffect()` hook will run after every completed render, but you can set it to only run if a certain value has changed. `useEffect()` receives two parameters: a function, and the second argument is the configuration of when the first parameter function should be called.

If you pass an empty array as a second parameter, the function should be called only the first time the complete render happens. If you pass one or more variables names as elements of the array passed as the second argument to `useEffect()`, every time there is a change in the value of any of those variables, the function passed as a first argument to `useEffect()` will be called.

In our case, the function passed as a first argument to `useEffect()` , will be called only the first time the DOM renders, as we are passing an empty array as a second argument to `useEffect(() => { ... }, [])`. You can learn more about the [useEffect() hook here](https://reactjs.org/docs/hooks-reference.html#useeffect).

Every time the `useEffect(() => { ... }, [])` hook is called, the `fetchArticles()` function will be called, which will fetch the articles' data from the back end NodeJS API of [this project](https://github.com/anayib/react-node-blog-app).

Once the `fetchArticles()` is called, the program in the body of this fuction will call the `setArticles(content.data);` function, which receives, as an argument, the `content.data` data fetched from the API, and will set the returned value from `content.date` as the updated value of `articles`.

This is how the `useEffect()` hook allow us to listen to new renders of the DOM, and execute an action once or every time there is a change in the mounted DOM, or any specific variable that we want to pass to the `useEffect()` hook as a second argument.

### Returning the Context Provider that will Give Access to the State to Other Components

Once we have a clear understanding of how to handle the state of our articles, we now need to return what is required so that we can make the `articles` state available to other components. To do so, we need to have access to our _Provider_ React component, so that we can share the data that is initialized and handled in the `ContextProvider` component with other components.

Every React _Context_ object has two components as methods when creating it by using the React API `React.createContext()` function:

- The _Provider_ method - A component that provides the value
- The _Consumer_ method - A component that is consuming the value

The _Provider_ React component allows children components to consume any data the _Provider_ has access to.

The way you make the state of the `ContextProvider` component available is by returning a `Context.Provider` React component, and passing a `value` prop containing the `articles` data, in order to make it available to any consuming components that are decendants of this _Provider_.

What?! I know, it seems confusing, but it is actually simple. Let's go trough the code in chunks to make it clearer:

When calling the `<Context.Provider />` component, and passing the variables you include in the `value` props to that _Provider_ component, which in our case is the `articles` variable, you will give any descendant component that might be wrapped by the _Provider_ access to that variable.

If we log the `<Context.Provider />` component to the console for our project example, you will see the following:

```javascript
[Click to expand] <Context.Provider />
  Props: {value: {…}, children: {…}}
    value: {articles: Array(2)}
    ...
  Nodes: [div.wrapper]
```

Do not get scared about the details; what you see above is basically the _Provider_ component which has access to the data you have given access to via the `value` prop.

To sum it up, you need to return a _Provider_ component from your _ContextProvider_ component, with the data that you need to make available to other `children` components: `return <Context.Provider value={{ articles }}>{children}</Context.Provider>;`

For instance, all the components wrapped in the `<ContextProvider />` component below, will have access to the _Context_ data ([check out the file in the repo](https://github.com/anayib/react-node-blog-app/blob/master/client/index.js)) :

```javascript
<ContextProvider>
  /* all the children components called here will have access to the data from
  the ContextProvider component */
</ContextProvider>
```

If the above is overwhleming, don't worry. Read it again. The take-away is that you need to wrap all the `children` elements that will need access to the data from your _Provider_ in the _Context.Provider_ component.

Take a break...

The next section is similar to this one, but it explains the `<ContextProviderSearch />` component I created to handle the data of a given search.

## The Use of Context as a Way to Separate Concerns and Handle Data

As a separate concern in our application, we will need a new _Context_ that handles the state of the `articles` that should be shown to the user when a given search query takes place.

I have called this new _Context_ the _ContextProviderSearch_. It depends on the `articles` data from the `Context.js`.

Let's take a look at the [SearchContext.js](https://github.com/anayib/react-node-blog-app/blob/master/client/SearchContext.js) file to understand how the _Context_ object from the previous section is used to access the `articles` in this case:

```javascript
import React, { useState, useContext } from "react";
// code omitted
import { Context as AppContext } from "./Context"; // imports the Context provided by Context.js
const Context = React.createContext();
// code omitted

function ContextProviderSearch({ children }) {
  // code omitted
  const { articles } = useContext(AppContext); // Access the articles array from the Context.js file

  // code omitted

  return (
    <Context.Provider
      value={
        {
          /*all the props that will be required by consumer components*/
        }
      }
    >
      {/* any consumer component*/}
    </Context.Provider>
  );
}

export { ContextProviderSearch, Context };
```

The most important lines of this file for our purpose are `import { Context as AppContext } from "./Context"` and `const { articles } = useContext(AppContext)`.

The `import { Context as AppContext } from "./Context"` helps us import the context from our `Context,js` file.

The `const { articles } = useContext(AppContext)` expression uses the `useContext()` React hook, which accepts the `AppContext` as an argument, and returns the current context value we imported from `Context.js`. Using the deconstruct JavaScript feature, we create a constant with the `articles` array, to which the `AppContext` has access to.

This way, our `ContextProviderSearch` now has access to the _Context_ from `Context.js`.

In brief, you can use the `useContext` React hook to have access to any _Context_ you have created in your application in order to access the state that the given _Context_ manage.

The [SearchContext.js](https://github.com/anayib/react-node-blog-app/blob/master/client/SearchContext.js) file includes some logic that is out of the scope of this article. If you have any questions about it, just [ask me](http://www.nayibabdala.com/contact).

## Things to be Improved in This Project

I created this project with an educational objective. There are several things that could be improved. I'm going to list some of them below, in case you are curious or have alrady identified them while checking the repo:

- Testing: Additional unit tests should be added to check that the contexts data management is well. Also, adding tests to the back end NodeJS API would be a good idea.
- Data Storage: For educational purposes, it is ok to store the articles in the file system. Nevertheless, it would be a better idea to integrate an SQL or NoSQL database to the project. Some options are Posgres with Squelize as a ORM, or MongoDB with Mongoose as a DRM.
- Browser data Storage: The `articles` data is temporarly stored in the `Window.localStorage` storage object once it is fetched from the `Context.js` via the NodeJS API. The `Window.localStorage` has a storage size limit that might be not enough when handling several articles.
- Lazy load: You could add the [Lazy Loading](https://webpack.js.org/guides/lazy-loading/) utility to improve the size of the files created by webpack.
- Add API authentication
- Implement error boundaries
- Implement type-checking for the React application

If you are not familiar with the concepts from the list above, check them out and try to implement them by cloning the repository. The exercise will strengthen your React skills.

## The Repository

You can find the [Open-Source project here](https://github.com/anayib/react-node-blog-app).

I hope this article and project will serve as a reference for you to understand how to use _Context_ and _Hooks_ in your React apps.

Article originally posted at www.nayibabdala.com