Create a react app using context, hooks and reducers
Tags: react, javascript, hooks, context
Published: 2022-01-24 20:22:55

Yes, I know what you're thinking. This isn't just another todo list tutorial, is it?
In this post, I'll be talking about a little advanced concept called React _**Context**_ and how to use it in an application by building a very basic todo list app.

## Expectations
This post assumes a basic understanding of the react framework.

Let's get started

---


## What is Context?
According to official react [documentation](https://reactjs.org/docs/context.html)
> Context provides a way to pass data through the component tree without having to pass props down manually at every level.


## Why/When do we need to use Context?
React [documentation](https://reactjs.org/docs/context.html) to the rescue again
> In a typical React application, data is passed top-down (parent to child) via props, but such usage can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.

---

Okay, enough with the introduction. Now we build a basic react app using create-react-app starter kit.
For those who would like to see the finished app, here is a preview of the [complete code](https://codesandbox.io/s/redux-todolist-up2xo?file=/src/TodoContext.js)


First create a `Todo.js` file inside the `src` directory and add the following code to it. This component will render a button and a [controlled input](https://reactjs.org/docs/forms.html#controlled-components)

```tsx
import React, { useState } from "react";

export default function Todo() {
  const [todo, setTodo] = useState("");

  return (
    <form>
      <input
        type="text"
        name="todo"
        value={todo}
        onChange={(e) => setTodo(e.target.value)}
      />
      <button>Add todo </button>
    </form>
  );
}

```
 
Next create a `TodoList.js` file and the `src` directory. This component will render our todo list, but for now it simply renders a `h1` heading for the list.

```tsx
import React from "react";

export default function TodoList() {
  return (
    <div>
      <h1> Todo List </h1>
    </div>
  );
}

```

Now we are getting to the interesting part.
To use Context in a react app, we need to create the context. So create a `TodoContext.js` in the same `src` directory.
Add the following line of code to it. Yes, I know we don't have a `./reducer.js` file yet. Don't worry, we will create it soon.

```tsx
import React, { createContext, useReducer } from "react";
import reducer, { initialState } from "./reducer";

```
Create a context named `todoContext` by adding the next line of code

```
export let todoContext = createContext({});
```

Next create function component `Provider` and pass down `children` as props to it.

```
export default function Provider({ children }) {

  const [state, dispatch] = useReducer(reducer, initialState);

  const values = {
    state,
    dispatch
  };

  return (
    <>
      <todoContext.Provider value={values}>
         {children}
      </todoContext.Provider>
    </>
  );
}

```

let's explain the code above. 
1. useReducer - The useReducer Hook is similar to the useState Hook. It allows for custom state logic.
If you find yourself keeping track of multiple pieces of state that rely on complex logic, useReducer may be useful.
This hook accepts two arguments, a reducer and an initial state.
2. Our `Provider` component returns a context.Provider component which accepts a value prop and passes it down to the children of our `Provider` component.

Here is the full code for the `TodoContext.js`

```tsx
import React, { createContext, useReducer } from "react";
import reducer, { initialState } from "./reducer";

export let todoContext = createContext({});

export default function Provider({ children }) {

  const [state, dispatch] = useReducer(reducer, initialState);

  const values = {
    state,
    dispatch
  };

  return (
    <>
      <todoContext.Provider value={values}>
        {children}
      </todoContext.Provider>
    </>
  );
}

```
Let's create our `reducer.js` file and add the code below to it

```tsx
import { ADD_TODO } from "./constants";

export const initialState = {
  todos: []
};

const reducer = (state, action) => {
  switch (action.type) {
    case ADD_TODO:
      return {
        ...state,
        todos: [...state.todos, action.payload]
      };
    default:
      return state;
  }
};

export default reducer;

```
The above code creates an initial state and initializes todos to an empty array. We then added a reducer function, a reducer function accepts a state and action as arguments and returns the updated state based on the type of action dispatched, which in our case we have an action called _ADD_TODO_ that will be defined in `constants.js`.
Create the file `constants.js` and add following code to it

```tsx
export const ADD_TODO = "ADD TODO";
```
As we can see, constants.js simply creates and exports a string variable.

Next we update the code inside our `app.js` file by importing the `TodoContext.js`, `Todo.js` and `TodoList.js` component.
`app.js` now should look like this

```tsx
import React, { Component } from "react";
import Todo from "./Todo";
import TodoList from "./TodoList";
import Provider from "./TodoContext";

export default class App extends Component {
  render() {
    return (
      <div className="App">
        <Provider>
          <Todo />
          <TodoList />
        </Provider>
      </div>
    );
  }
}

```

Since we have wrapped our components in a Provider component, we can now be sure that we have access to the context in our `Todo.js` and `TodoList.js`.
We can update out `Todo.js` file to now access the context value and also create a function `addTodoHandler` that dispatches the ADD_TODO action type.
Below is the complete code for `Todo.js`

```tsx
import React, { useState, useContext } from "react";
import { todoContext } from "./TodoContext";
import { ADD_TODO } from "./constants";

export default function Todo() {
  const [todo, setTodo] = useState("");
  const { dispatch } = useContext(todoContext);

  const addTodoHandler = (e) => {
    e.preventDefault();
    dispatch({ type: ADD_TODO, payload: todo });
    setTodo("");
  };

  return (
    <form>
      <input
        type="text"
        name="todo"
        value={todo}
        onChange={(e) => setTodo(e.target.value)}
      />
      <button onClick={addTodoHandler}>Add todo </button>
    </form>
  );
}

```
Cool. Now all we need to do is display our Todo whenever a user clicks the _Add todo_ button. And we do this by mapping over our todos array defined in the _reducer.js_ and returning the jsx.
Below is the complete code for the `TodoList.js`

```tsx
import React, { useContext } from "react";
import { todoContext } from "./TodoContext";

export default function TodoList() {
  const { state } = useContext(todoContext);
  return (
    <div>
      <h1> Todo List </h1>
      {state.todos.length > 0 &&
        state.todos.map((todo, id) => {
          return <h4 key={id}>{todo}</h4>;
        })}
    </div>
  );
}

```

---


That's it. Now you have learnt what context means, when to use it and what a reducer is and most importantly, you've learned how to implement them in an application, albeit a small one :).

Disclaimer: To build a todo app like this one, using context and reducer is kind of overkill is unnecessary. But to better demonstrate and explain these concepts, I had to choose an example that won't be too complex to understand.

Thank you for reading, you can reach out to me for suggestions or corrections.