React Context — Simplified



React components can store a set of observable properties called states. React can decide how to render itself based on the state values. Sometimes it is important to share the state between multiple components by passing the state down through the component tree. When the component tree is too deep and the application becomes bigger, passing states using props through all these components creates a big mess.


Context aims to solve this issue. It stores all states in a central place and shares the states between the components without having to pass them down through the component tree. React Context API manages the state of the application in one place. It also, makes the state accessible to all components.


Problem

To summarize react Context API, I will create an application and show you how to add context to it.

I will create a todo list application using react only. For backend I will not create anything. I will take advantage of {JSON} Placeholder.


Requirements

This tutorial requires simple react concepts from the readers.


Project Setup

Use the bellow command to create a react.js application.

npx create-react-app react-context-demo


Removing Unnecessary Files and Code

Remove all files inside src folder except App.js and index.js.


Enabling React Context

To enable context in react application we need to follow these steps:

  • Creating Context

  • Using Reducer

  • Providing Context

  • Using Context


Creating Context

React.js provides createContext() hook. This hook receives an argument as initial state and returns a context. In our example we create a context and passing initial state containing an empty list of todos to it.

  • Create a file inside srcfolder named TodoContext.js and add the bellow code.

import React, { createContext } from 'react';
const initialState = {
   todos: []
};
export const TodosContext = createContext(initialState);


Using Reducer

Reducer is a function receives two arguments state and action and returns a plain JavaScript object. Usually action has two variables type and payload.

  • Create a file named TodoRecuer.js inside src folder.

import React, { useReducer }  from 'react';
function todosReducer(state, action) {
   switch(action.type) {
      case "FETCH_TODOS":
         return {
            ...state,
            todos: action.payload,
         };
      case "CREATE_TODO":
         return {
            ...state,
            todos: [...state.todos, action.payload],
         };
      default:
         return state;
   }
}
  • Later we use this reducer using useReducer hook. It returns tuple with two values:

  1. State: it is the state of the application. It is required to pass to Provider.

  2. Dispatch: it is required to dispatch reducer actions.

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


Providing Context

The context which has been created using createContext hook, in our example TodoContext comes with a context provider TodoContext.Provider.

  • We have to wrap the whole app with the provider. For that reason we create a component which returns TodoContext.Provider with children.

  • Create a react component (a JavaScript function) inside TodoContext.js file named TodoContextProvider.

  • Reducer actions are also defined inside TodoContextProvider component. They call dispatch method which comes from useReducer hook.

// Create Provider
export function TodoContextProvider({ children }) {
  // Use reducer
  const [state, dispatch] = useReducer(todoReducer, initialState);
  
  // Define reducer actions
  function fetchTodos(todos) {
    dispatch({
      type: "FETCH_TODOS",
      payload: todos,
    });
  }
  
  function saveTodo(todo) {
    dispatch({
      type: "SAVE_TODO",
      payload: todo,
    });
  }  return (
    <TodoContext.Provider
      value={{
        todos: state.todos,
        fetchTodos,
        saveTodo,
      }}
    >
    {children}
    </TodoContext.Provider>
  );
}
  • In our index.js file wrap the App component with TodoContextProvider.

<TodoContextProvider>
  <App />
</TodoContextProvider>


Using Context

There are multiple ways to use context inside a component and get the state or update it.

  • An example of getting the state.

import React, { useContext, useEffect } from "react";
import { TodoContext } from "../TodoContext";export default function ListTodos() {
  const { todos } = useContext(TodoContext);
  return (
    <div className="col-8">
      <table className="table table-hover">
        <thead>
          <tr>
            <th>#</th>
            <th>Title</th>
            <th>Completed</th>
            <th></th>
          </tr>
        </thead>
      <tbody>
      {todos.map((todo, index) => (
        <tr key={index}>
          <td>{index + 1}</td>
          <td>{todo.title}</td>
          <td 
            className={todo.completed
              ? "text-primary"
              : "text-warning"}
          >
            {todo.completed ? "completed" : "uncompleted"}
          </td>
        </tr>
      ))}
      </tbody>
    </table>
  </div>);
}
  • An example of updating the state.

import React, { useContext, useState } from 'react';function CreateTodo() {
   const { createTodo } = useContext(TodoContext);
   const [title, setTitle] = useState('');   function handleClick() {
      createTodo({
         title
      });
   }   return (
      <>
         <input
            type="text"
            onChange={e => setTitle(e.target.name)}
         />
         <button onClick={handleClick}>Save</button>
      </>
   );
}


Conclusion

React Context is a powerful tool when using inside React applications. It simplifies retrieving the state or updating it without the need to pass props down the component hierarchy. More importantly, react context separates the UI from the state.


Source: Medium


The Tech Platform

www.thetechplatform.com

0 comments