Trong React, dữ liệu thường được truyền từ phần tử cha xuống phần tử con thông qua props. Nhưng điều này có thể dẫn đến “prop drilling” – khi chúng ta phải truyền props xuống nhiều thành phần để đưa chúng đến nơi cần thiết.
Ngoài ra, một số props (ví dụ: người dùng đã xác thực hiện tại, chủ đề UI hoặc ngôn ngữ ưa thích) sẽ được nhiều thành phần trong ứng dụng yêu cầu.
API Context của React cung cấp một cách để chia sẻ các giá trị như thế này giữa các thành phần mà không cần phải truyền chúng xuống một cách rõ ràng dưới dạng props qua mọi cấp độ của tree. Vì vậy, Context được thiết kế để chia sẻ dữ liệu có thể được coi là “toàn cục” cho một tree của component React.
React Context API là gì và khi nào bạn nên sử dụng nó?
Context API là một tính năng trong React cung cấp một cách để chia sẻ các giá trị như chủ đề, thông tin người dùng hoặc cài đặt cấu hình giữa các component mà không cần phải truyền props một cách rõ ràng qua mọi cấp độ của tree component. Điều này làm cho nó đặc biệt hữu ích để quản lý state toàn cục hoặc state cần thiết cho nhiều component ở các cấp độ lồng nhau khác nhau.
Context API là một phần của thư viện React, nghĩa là bạn không cần phải cài đặt nó dưới dạng gói của bên thứ ba trong ứng dụng React.
Vì vậy, Context API có thể được sử dụng để chia sẻ các biến toàn cục giữa các thành phần trong ứng dụng React mà không cần phải truyền các biến này dưới dạng props xuống tree component. Điều này đặc biệt hữu ích nếu có các component được lồng nhau sâu cần truy cập vào các biến từ các component cao hơn.
Bây giờ, chúng ta hãy tìm hiểu cách Context API hoạt động bằng cách xem qua một ví dụ về trường hợp sử dụng phổ biến cho Context API…
Ví dụ về React Context API — Chủ đề giao diện người dùng chế độ sáng và tối(Light and Dark Mode UI Theme)
Một trường hợp sử dụng thực tế rất phổ biến cho React Context API là để lưu trữ chủ đề ưa thích của người dùng hiện tại – tức là “chế độ sáng” hoặc “chế độ tối”.
Hãy nghĩ về điều này: nhiều thành phần UI trong ứng dụng React sẽ cần biết về chủ đề hiện tại để hiển thị các kiểu phù hợp. Nút, Tiêu đề, Thanh điều hướng, Chân trang, Menu thả xuống – rất nhiều thành phần sẽ cần hiển thị theo cách khác nhau tùy thuộc vào chủ đề hiện tại.
Giải pháp truyền xuống prop
Cách đơn giản và rõ ràng nhất để giải quyết vấn đề này trong React là tạo một biến “theme” trên đầu “App Component”, sau đó tiếp tục truyền nó xuống dưới dạng “prop” cho tất cả các component trong tree. Nhưng điều này dẫn đến một vấn đề trong React được gọi là “prop drilling”.
Prop drilling là một thuật ngữ được sử dụng trong React để mô tả quá trình truyền dữ liệu từ component cha sang component con được lồng sâu thông qua nhiều component trung gian. Điều này có thể xảy ra khi bạn cần truyền state hoặc function xuống nhiều cấp trong component tree.
Ví dụ về “Prop drilling”
function App() {
const theme = 'dark';
return <Parent theme={theme} />;
}
function Parent({ theme }) {
return <Child theme={theme} />;
}
function Child({ theme }) {
return <Button theme={theme} />;
}
function Button({ theme }) {
return <button style={{ background: theme === 'dark' ? 'black' : 'white' }}>Click me</ button >;
}
Như bạn có thể thấy, mỗi component trung gian cần bao gồm prop, ngay cả khi không sử dụng prop, chỉ để truyền nó xuống xa hơn. Điều này làm lộn xộn code và khiến code khó hiểu hơn.
Ngoài ra, các tcomponent trung gian không sử dụng prop vẫn có thể render lại khi prop thay đổi, dẫn đến các vấn đề về hiệu suất. Điều này có thể đặc biệt có vấn đề trong các ứng dụng lớn có tree component sâu.
Context API sẽ giải quyết vấn đề này
Tạo context
Đầu tiên, chúng ta cần tạo context và truyền vào theme sáng(light) làm giá trị mặc định:
// src/contexts/ThemeContext.js
import { createContext } from "react";
export const themes = {
light: {
background: "white",
text: "black",
},
dark: {
background: "black",
text: "white",
},
};
export const ThemeContext = createContext(themes.light);
Ở trên, chúng tôi đã tạo một thư mục contexts bên trong thư mục src của mình để lưu trữ tất cả các contexts. Việc tạo từng context trong tệp riêng của nó được coi là một thói quen tốt. Trong trường hợp của chúng tôi, chúng tôi chỉ cần tạo một context để lưu trữ theme hiện tại.
Lưu ý rằng các context được tạo bằng cách gọi hàm createContext() có trong thư viện React. Chúng tôi truyền cho hàm createContext() một giá trị mặc định là themes.light.
Gắn context cho trang
Tiếp theo, chúng ta cần bao bọc tất cả các component cần truy cập vào theme trong context provider. context provider lấy prop “value”, nơi chúng ta có thể truyền giá trị mà chúng ta muốn biến thành toàn cục.
Bên dưới,<button> </button> sẽ có quyền truy cập vào “theme” state, mặc dù chúng ta chưa truyền rõ ràng nó xuống dưới dạng prop. Điều này là do chúng ta đã bao bọc các component này trong context provider theme và truyền cho nó giá trị (theme) cần biến thành toàn cục.
// src/App.js
import React, { useState } from "react"
import { ThemeContext, themes } from "./contexts/ThemeContext"
import Navbar from "./components/Navbar"
import Button from "./components/Button"
const App = () => {
const [theme, setTheme] = useState(themes.light)
const toggleTheme = () => {
setTheme(state => (state === themes.light ? themes.dark : themes.light))
}
return (
<ThemeContext.Provider value={theme}>
<Navbar />
<Button changeTheme={toggleTheme} />
</ThemeContext.Provider>
)
}
export default App
Nếu chúng ta cũng muốn setTheme() khả dụng trong toàn bộ ứng dụng của mình thông qua context, chúng ta có thể truyền đối tượng sau cho “value” prop. Sau đó, chúng ta có thể chuyển đổi theme từ bất kỳ component nào trong Theme Context Provider:
<ThemeContext.Provider value={{ theme, setTheme }} >
Bây giờ chúng ta hãy tạo các Component Button và Navbar sẽ sử dụng context “theme” bằng cách sử dụng hook useContext(). Lưu ý cách styles CSS của các component thay đổi tùy thuộc vào các giá trị theme hiện tại:
// src/components/Button.js
import React, { useContext } from "react"
import { ThemeContext } from "../contexts/themeContext"
const Button = ({ changeTheme }) => {
const theme = useContext(ThemeContext)
return (
<button style={{ backgroundColor: theme.background, color: theme.text }} onClick={changeTheme} >
Toggle theme
</ button >
)}
export default Button
// src/components/Navbar.js
import React, { useContext } from "react"
import { ThemeContext } from "../contexts/themeContext"
const Navbar = () => {
const theme = useContext(ThemeContext)
return (
<nav style={{ backgroundColor: theme.background }}>
<ul>
<li style={{ color: theme.text }}>Home</li>
<li style={{ color: theme.text }}>About</li>
</ul>
</nav>
)}
export default Navbar
Đây là kết quả bạn nhận được khi chạy chương trình.
Bạn có thể tạo nhiều React Contexts đồng thời
Trong ví dụ trên, chúng ta chỉ tạo một ngữ cảnh, ThemeContext. Nhưng nếu chúng ta có dữ liệu khác cần được tạo thành toàn cục, chẳng hạn như tên người dùng và tuổi của người dùng hiện đang đăng nhập thì sao?
Chúng ta có thể chỉ cần tạo một ngữ cảnh lớn để lưu trữ tất cả các biến cần được sử dụng toàn cục:
< OneBigContext.Provider value={{ theme, username, age }} >
< Button changeTheme={toggleTheme} />
< Navbar />
</ OneBigContext.Provider >
Nhưng điều này được coi là triển khai không tốt, vì bất cứ khi nào giá trị context được cập nhật, tất cả các component sử dụng context đó sẽ được render lại. Điều này có nghĩa là tất cả các component chỉ cần biết về theme, chứ không phải các biến người dùng,sẽ được render lại bất cứ khi nào “bất kỳ biến người dùng nào” được cập nhật. Điều này có thể làm giảm hiệu suất của ứng dụng, đặc biệt là trong các ứng dụng lớn hơn với nhiều thành phần phức tạp.
Chúng ta có thể giải quyết vấn đề này bằng cách tạo nhiều ngữ cảnh – một context cho theme và một context khác cho dữ liệu người dùng – và gói ứng dụng của chúng ta trong cả hai provider, như sau:
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={{ username, age }}>
<Button changeTheme={toggleTheme} />
<Navbar />
</UserContext.Provider>
</ThemeContext.Provider>
Bằng cách chỉ lưu trữ dữ liệu liên quan trong mỗi context, chúng tôi giúp ngăn chặn việc render lại các component không cần thiết và cải thiện hiệu suất của ứng dụng.
Nguồn dịch: https://www.freecodecamp.org/news/react-context-api-tutorial-examples