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:
State: it is the state of the application. It is required to pass to Provider.
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
Bình luận