16.7 C
New York

Create a react app using context, hooks and reducers

Date:

Yes, I know what you’re thinking. This isn’t just another todo list tutorial, is it?
In this post, I’ll be explaining 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

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 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

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

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.

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.

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

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

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

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

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

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

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 learnt 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 an overkill and is unnecessary. But to better demonstrate and explain this concepts, I had to choose an example that won’t be too complex to understand.

Edit: As suggested by BilalurRehman in the comments, it is not a very good practice to use index as key in the TodoList.js file, it is better to use the
UUID() library.

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

  • Coinsmart. Europe’s Best Bitcoin and Crypto Exchange.Click Here
  • Platoblockchain. Web3 Metaverse Intelligence. Knowledge Amplified. Access Here.
  • Source: https://www.codementor.io/majidk/create-a-react-app-using-context-and-hooks-and-reducers-1pw95clio1

This Post was originally published on Codementor React Fact

Related articles

spot_img

Recent articles

spot_img