Thông thường trong React, bạn sẽ truyền data từ component cha xuống component con thông qua props. Nhưng việc chuyển data thông qua props có thể sẽ trở nên dài dòng và bất tiện nếu bạn phải chuyển chúng qua nhiều component (nhiều level component) hoặc nhiều component trong ứng dụng cần sử dụng cùng 1 thông tin (vd: thông tin user đăng nhập, theme). Việc này có thể dẫn đến tình huống sẽ có nhiều component bị re-render lại 1 cách không cần thiết do giá trị của props bị thay đổi.
Chúng ta cùng đi vào phân tích 1 trường hợp cụ thể dưới đây
Giả sử chúng ta đang có User Data đang được truyền từ component E xuống component G (là con của E). Tuy nhiên sau đó component J lại muốn sử dụng User Data thì chúng ta làm như nào?
Để giải quyết yêu cầu ở trên theo cách thông thường(truyền data thông qua props) thì chúng ta phải chuyển User Data lên thành state của Root Component sau đó truyền xuống các component con thông qua props(như hình bên dưới).
Tuy nhiên, việc chuyển state lên component Root sẽ dẫn đến vấn đề gọi là “prop drilling“. Nếu sử dụng phương án trên chúng ta sẽ thấy vấn đề mỗi khi User Data thay đổi thì các component A, C, H cũng sẽ bị re-render lại trong khi các component này lại không sử dụng User Data. Để giải quyết vấn đề này, React giới thiệu khái niệm Flux (Flux concept) và Context API.
Context cho phép một component cha cung cấp data cho toàn bộ cây bên dưới nó (tree components) mà không cần phải truyền data thông qua props. Điều này giúp cho việc chia sẻ data giữa các component trở nên dễ dàng hơn. Chúng ta có thể lưu trữ dữ liệu toàn cục (global data) vào trong Context và lấy ra dữ liệu ở các component cần dữ liệu đó.
Để sử dụng dữ liệu được lưu trữ trong context, bạn cần cung cấp Context Provider trong parent component (trong ví dụ trên là Root component) và cần sử dụng Context Consumer ở các component con cần lấy ra dữ liệu từ context (trong ví dụ trên là component G, J).
Bây giờ chúng ta đi vào implement code cụ thể cho ví dụ trên
Bước 1: Khởi tạo context
Đầu tiên chúng ta tạo 1 thư mục mới đặt tên là context, sau đó tạo 1 file mới trong thư mục context và đặt tên là AppContext với nội dung như sau:
//AppContext file
import { createContext } from "react";
export const AppContext = createContext(null);
Hàm createContext
sẽ trả về 1 context object, các component có thể đọc giá trị của context bằng cách sử dụng hàm useContext
và truyền vào tham số là context object (cụ thể trong ví dụ này là AppContext).
Chúng ta sẽ set giá trị mặc định khi khởi tạo context là null, và các bạn có thể set giá trị default khác tùy mong muốn của các bạn. Lưu ý giá trị default sẽ không bao giờ thay đổi.
Bước 2: Khai báo Context Provider và set giá trị cho Context Provider
Ở file App (root component), chúng ta cần import file AppContext
đã được khai báo ở bước 1.
import ComponentA from "./components/ComponentA";
import ComponentH from "./components/ComponentH";
import "./styles.css";
import { AppContext } from "./context/AppContext";
import { useState } from "react";
export default function App() {
const [currentUser, setCurrentUser] = useState({ name: "Hoang Huu" });
return (
<AppContext.Provider value={currentUser}>
<div className="App">
<div className="component">
<div className="box">Root Component</div>
<div className="flex">
<ComponentA />
<ComponentH />
</div>
</div>
</div>
<div style={{ marginTop: "20px" }}>
<label>User name: </label>
<input
type="text"
placeholder="Enter user name..."
value={currentUser.name}
onChange={(ev) => {
setCurrentUser({ name: ev.target.value });
}}
/>
</div>
</AppContext.Provider>
);
}
Ở đoạn code trên mình đang truyền giá trị từ State currentUser vào cho context Provider để có thể lấy ra sử dụng trong các component cần thiết.
Bước 3: Đọc value từ context và sử dụng trong các component
Để đọc value từ context chúng ta sử dụng hook function useContext
của React với tham số truyền vào component này là AppContext
import { useContext } from "react";
import { AppContext } from "../context/AppContext";
const ComponentG = () => {
const contextData = useContext(AppContext);
return (
<div className="component">
<div className="box">
Component G
{contextData ? (
<div className="context-value-box">
Context data:{contextData.name}
</div>
) : null}
</div>
</div>
);
};
export default ComponentG;
Và kết quả cuối cùng các bạn có thể xem tại đây
Các trường hợp nên sử dụng Context API
Context chủ yếu được sử dụng khi một số dữ liệu cần được truy cập bởi nhiều thành phần ở các cấp độ lồng nhau khác nhau. Dưới đây là 1 số trường hợp các bạn nên sử dụng Context API
- Theming: bạn có thể sử dụng Context API để lưu trữ giá trị theme hiện tại của ứng dụng và có thể sử dụng nó ở tất cả các component mà bạn muốn.
- User Authentication: bạn có thể sử dụng Context API để lưu trữ thông tin của user hiện tại đang login trên ứng dụng và kiểm tra quyền truy cập các trang dựa theo permission của user.
- Multilingual: nếu trang web của bạn có hỗ trợ nhiều ngôn ngữ thì bạn cũng có thể sử dụng Context API để lưu trữ trạng thái được chọn của ngôn ngữ hiện tại.
- Truy cập dữ liệu từ những nguồn ngoài (external resources): bạn cũng có thể sử dụng Context API để lưu trữ dữ liệu được truy xuất từ các nguồn bên ngoài như API, cơ sở dữ liệu và cấp cho tất cả các component nếu cần. Ví dụ: dữ liệu về danh sách các quốc gia, đầu số điện thoại,…
- Sử dụng Context API cho các form phức tạp có nhiều step (Wizard form).
Nếu bạn có góp ý gì về bài viết, bạn có thể comment bên dưới.