paint-brush
Cách đơn giản hóa việc quản lý trạng thái với API ngữ cảnh React.js - Hướng dẫntừ tác giả@codebucks
Bài viết mới

Cách đơn giản hóa việc quản lý trạng thái với API ngữ cảnh React.js - Hướng dẫn

từ tác giả CodeBucks11m2024/08/02
Read on Terminal Reader

dài quá đọc không nổi

Blog này cung cấp hướng dẫn toàn diện về cách quản lý trạng thái trong React bằng API ngữ cảnh. Nó giải thích cách tránh việc khoan prop, nâng cao hiệu suất và triển khai API bối cảnh một cách hiệu quả. Với các ví dụ thực tế và mẹo tối ưu hóa, nó hoàn hảo cho các nhà phát triển muốn hợp lý hóa việc quản lý trạng thái trong ứng dụng React của họ.
featured image - Cách đơn giản hóa việc quản lý trạng thái với API ngữ cảnh React.js - Hướng dẫn
CodeBucks HackerNoon profile picture
0-item

Xin chào👋🏻,


Bài viết này được tạo riêng cho những người mới bắt đầu muốn tìm hiểu các phương pháp hiệu quả hơn để quản lý trạng thái giữa nhiều thành phần. Nó cũng nhằm mục đích giải quyết vấn đề phổ biến về việc khoan chống đỡ có thể khiến mã của bạn khó bảo trì và khó hiểu hơn. Hãy bắt đầu với loại vấn đề mà API ngữ cảnh giải quyết.


Nếu bạn thích định dạng video thì đây là hướng dẫn mà bạn có thể xem trên kênh YouTube của tôi.👇🏻


Khoan Prop là gì?

Bạn có biết đôi khi bạn cần truyền dữ liệu từ thành phần cha mẹ xuống thành phần con và cuối cùng bạn lại truyền đạo cụ qua một loạt thành phần ở giữa không? Đó gọi là khoan chống đỡ và nó có thể trở nên lộn xộn nhanh chóng. Hãy xem qua một ví dụ để làm rõ điều này.


Khoan đạo cụ trong React.js

Như được hiển thị trong sơ đồ, hãy tưởng tượng bạn đã tìm nạp một số dữ liệu trong thành phần App nằm ở thư mục gốc của ứng dụng. Bây giờ, nếu một thành phần được lồng sâu, chẳng hạn như thành phần Grandchild , cần truy cập dữ liệu này, thì bạn thường chuyển nó qua các thành phần ParentChild dưới dạng đạo cụ trước khi đến được Grandchild . Điều này có thể trở nên tồi tệ khi ứng dụng của bạn phát triển.


Đây là một cách trình bày trực quan khác:


Ví dụ khoan đạo cụ Reactjs

Trong ví dụ trên, thành phần Profile cần dữ liệu người dùng, nhưng dữ liệu này trước tiên phải truyền qua các thành phần AppNavigation , mặc dù bản thân các thành phần trung gian này không sử dụng dữ liệu. Vì vậy, làm thế nào để chúng ta làm sạch điều này? Đó là lúc API ngữ cảnh phát huy tác dụng.


Đạo cụ khoan:

  • Tăng khả năng hiển thị lại các thành phần
  • Tăng mã soạn sẵn
  • Tạo sự phụ thuộc thành phần
  • Giảm hiệu suất

API bối cảnh phản ứng

API ngữ cảnh trong React.js cho phép bạn truyền dữ liệu giữa các thành phần mà không cần truyền dữ liệu dưới dạng đạo cụ qua từng cấp độ của cây thành phần. Nó hoạt động giống như một hệ thống quản lý trạng thái toàn cầu, nơi bạn xác định trạng thái của mình trong một đối tượng ngữ cảnh và sau đó bạn có thể dễ dàng truy cập nó ở bất kỳ đâu trong cây thành phần. Hãy hiểu điều này bằng một ví dụ.


API bối cảnh React.js

Như bạn có thể thấy trong sơ đồ, chúng ta có một đối tượng ngữ cảnh lưu trữ dữ liệu để nhiều thành phần có thể truy cập. Dữ liệu này được lấy từ API hoặc dịch vụ của bên thứ ba. Trước khi truy cập dữ liệu ngữ cảnh này trong bất kỳ thành phần nào, chúng ta cần bao bọc tất cả các thành phần yêu cầu dữ liệu này trong thành phần nhà cung cấp ngữ cảnh.


Nếu chúng ta chỉ cần truy cập dữ liệu trong các thành phần điều hướng và hồ sơ, thì chúng ta không cần phải gói thành phần ứng dụng. Khi bạn đã gói các thành phần có liên quan bằng ContextProvider , bạn có thể truy cập trực tiếp vào dữ liệu ngữ cảnh trong bất kỳ thành phần nào sử dụng nó. Đừng lo lắng nếu bạn vẫn chưa hiểu nó; hãy đi sâu vào mã và xem nó hoạt động.


Làm cách nào để sử dụng API ngữ cảnh?

Đầu tiên, hãy tạo một ứng dụng React bằng Vite.js . Chỉ cần sao chép các lệnh sau để thiết lập dự án.


 npm create vite@latest


  • Thêm tên dự án của bạn
  • Chọn phản ứng
  • Chọn bản đánh máy từ các tùy chọn
 cd project_name // to change to project directory npm install npm run dev


Sau đó, bạn có thể mở máy chủ phát triển http://localhost:5173 trong trình duyệt của mình.


Đầu tiên, hãy tạo các thư mục cần thiết. Đây là cấu trúc thư mục của dự án của chúng tôi.

 src | components | context


Trong thư mục thành phần, hãy tạo tệp Profile.jsx và thêm mã sau đây.

 import React from 'react' const Profile = () => { return ( <div>Profile</div> ) } export default Profile


Tạo thêm một thành phần có tên Navbar.jsx trong thư mục thành phần.

 import Profile from './Profile' const Navbar = () => { return ( <nav style={{ display: "flex", justifyContent: "space-between", alignItems: "center", width: "90%", height: "10vh", backgroundColor: theme === "light" ? "#fff" : "#1b1b1b", color: theme === "light" ? "#1b1b1b" : "#fff", border: "1px solid #fff", borderRadius: "5px", padding: "0 20px", marginTop: "40px", }}> <h1>LOGO</h1> <Profile /> </nav> ) } export default Navbar


Hãy nhập thành phần <Navbar /> này vào tệp App.jsx .

 import Navbar from "./components/Navbar"; function App() { return ( <main style={{ display: "flex", flexDirection: "column", justifyContent: "start", alignItems: "center", height: "100vh", width: "100vw", }} > <Navbar /> </main> ); } export default App;


Như vậy về cơ bản, thành phần <Profile /> là con của <Navbar /><Navbar /> là con của thành phần <App /> .

Thêm API ngữ cảnh

Hãy tạo tệp UserContext.jsx trong thư mục context . Thêm mã sau vào tập tin.


 import { createContext, useEffect, useState } from "react"; export const UserContext = createContext(); export const UserProvider = ({ children }) => { const [user, setUser] = useState(null); const fetchUserData = async (id) => { const response = await fetch( `https://jsonplaceholder.typicode.com/users/${id}` ).then((response) => response.json()); console.log(response); setUser(response); }; useEffect(() => { fetchUserData(1); }, []); return ( <UserContext.Provider value={{ user, fetchUserData }} > {children} </UserContext.Provider> ); };


  • Đầu tiên, chúng ta tạo một đối tượng UserContext trống bằng cách sử dụng createContext . Chúng tôi đảm bảo nhập nó từ react . Chúng ta có thể thêm các giá trị mặc định bên trong đối tượng bối cảnh, nhưng hiện tại chúng ta vẫn giữ nó là null.


  • Tiếp theo, chúng ta tạo UserProvider để trả về một nhà cung cấp bằng cách sử dụng UserContext , như UserContext.Provider . Nó bao quanh các thành phần con và trong giá trị, chúng ta có thể chuyển bất cứ thứ gì chúng ta muốn sử dụng trong các thành phần con.


  • Hiện tại, chúng tôi đang sử dụng API jsonplaceholder để tìm nạp dữ liệu người dùng. Trình giữ chỗ json cung cấp các điểm cuối API giả mạo cho mục đích thử nghiệm. Hàm fetchUserData chấp nhận id và sử dụng ID đó để tìm nạp dữ liệu người dùng. Sau đó, chúng tôi lưu trữ phản hồi ở trạng thái user .


  • Chúng tôi đang gọi hàm fetchUserData trong useEffect nên khi tải trang, nó sẽ gọi hàm và đưa dữ liệu vào trạng thái user .


Bây giờ, hãy sử dụng bối cảnh này trong thành phần <App /> . Gói thành phần <NavBar /> bằng cách sử dụng <UserProvider /> ; giống như đoạn mã sau:

 <UserProvider> <Navbar /> </UserProvider>


Hãy sử dụng trạng thái user trong thành phần <Profile /> . Để làm được điều đó, chúng tôi sẽ sử dụng hook useContext . Điều đó lấy UserContext và cung cấp các giá trị mà chúng ta đã chuyển trong UserProvider , chẳng hạn như trạng thái user và hàm fetchUserData . Hãy nhớ rằng, chúng ta không cần bọc thành phần <Profile /> vì nó đã có trong thành phần <Navbar /> vốn đã được gói cùng với nhà cung cấp.


Mở Profile.jsx và thêm đoạn mã sau.

 const { user } = useContext(UserContext); if (user) { return ( <span style={{ fontWeight: "bold", }} > {user.name} </span> ); } else { return <span>Login</span>; }


Ở đây, chúng tôi đang sử dụng trạng thái user từ UserContext . Chúng tôi sẽ hiển thị tên người dùng nếu có user , nếu không chúng tôi sẽ chỉ hiển thị thông báo đăng nhập. Bây giờ, nếu bạn thấy kết quả đầu ra thì phải có tên người dùng trong thành phần thanh điều hướng. Đây là cách chúng ta có thể trực tiếp sử dụng bất kỳ trạng thái nào trong ngữ cảnh của bất kỳ thành phần nào. Thành phần sử dụng trạng thái này phải được gói trong <Provider /> .


Bạn cũng có thể sử dụng nhiều bối cảnh. Bạn chỉ cần bọc các thành phần nhà cung cấp trong một thành phần nhà cung cấp khác như trong ví dụ sau.

 <ThemeProvider> <UserProvider> <Navbar /> </UserProvider> </ThemeProvider>


Trong ví dụ trên, chúng tôi đang sử dụng <ThemeProvider /> để quản lý trạng thái chủ đề.


Bạn có thể xem video youtube ở trên để xem ví dụ đầy đủ về việc sử dụng nhiều nhà cung cấp ngữ cảnh.

Tối ưu hóa kết xuất lại trong API bối cảnh phản ứng

Có một vấn đề xảy ra khi bạn sử dụng API ngữ cảnh trong nhiều thành phần. Bất cứ khi nào trạng thái hoặc giá trị thay đổi trong API ngữ cảnh, nó sẽ hiển thị lại tất cả các thành phần đã đăng ký với ngữ cảnh cụ thể đó, ngay cả khi không phải tất cả các thành phần đều đang sử dụng trạng thái đã thay đổi. Để hiểu vấn đề kết xuất lại này, hãy tạo một thành phần <Counter /> sử dụng ngữ cảnh để lưu trữ và hiển thị các giá trị đếm.


Hãy xem ví dụ sau. Bạn có thể tạo tệp Counter.jsx trong thư mục thành phần và dán đoạn mã sau.


 import { createContext, memo, useContext, useState } from "react"; const CountContext = createContext(); const CountProvider = ({ children }) => { const [count, setCount] = useState(0); return ( <CountContext.Provider value={{ count, setCount }}> {children} </CountContext.Provider> ); }; function CountTitle() { console.log("This is Count Title component"); return <h1>Counter Title</h1>; } function CountDisplay() { console.log("This is CountDisplay component"); const { count } = useContext(CountContext); return <div>Count: {count}</div>; } function CounterButton() { console.log("This is CounterButton component"); const { count, setCount } = useContext(CountContext); return ( <> <CountTitle /> <CountDisplay /> <button onClick={() => setCount(count + 1)}>Increase</button> </> ); } export default function Counter() { return ( <CountProvider> <CounterButton /> </CountProvider> ); }


Trong đoạn mã trên:

  • Đầu tiên, chúng ta tạo một đối tượng CountContext bằng createContext.


  • Trong CountProvider, chúng ta có một trạng thái để lưu trữ các giá trị đếm. Chúng ta đang gửi count và phương thức setCount tới các thành phần con thông qua value prop.


  • Chúng tôi đã tạo các thành phần riêng biệt để xem số lần hiển thị lại các thành phần riêng lẻ.

    • <CountTitle /> : Thành phần này chỉ hiển thị tiêu đề và thậm chí không sử dụng bất kỳ giá trị nào từ ngữ cảnh.

    • <CountDisplay /> : Thành phần này hiển thị các giá trị đếm và sử dụng trạng thái count từ ngữ cảnh.

    • <CounterButton /> : Thành phần này hiển thị cả thành phần trên và nút tăng giá trị đếm bằng cách sử dụng setCount .


  • Cuối cùng, chúng tôi gói thành phần <CounterButton /> trong thành phần CountProvider để các thành phần khác có thể truy cập vào các giá trị đếm.


Bây giờ, nếu bạn chạy mã và nhấp vào nút Increase , bạn sẽ thấy trong nhật ký rằng mọi thành phần đều hiển thị lại mỗi khi trạng thái thay đổi. <CountTitle /> thậm chí không sử dụng các giá trị đếm nhưng nó đang hiển thị lại. Điều này xảy ra vì thành phần chính của <CountTitle /><CounterButton /> đang sử dụng và cập nhật giá trị của count và đó là lý do hiển thị lại.


Làm thế nào chúng ta có thể tối ưu hóa hành vi này? Câu trả lời là memo . memo React cho phép bạn bỏ qua việc hiển thị lại một thành phần khi đạo cụ của nó không thay đổi. Sau thành phần <CountTitle /> , hãy thêm dòng sau.


 const MemoizedCountTitle = React.memo(CountTitle)


Bây giờ, trong thành phần <CounterButton /> , nơi chúng ta đang hiển thị thành phần <CountTitle /> , hãy thay thế <CountTitle /> bằng <MemoizedCountTitle /> như trong đoạn mã sau:


 <> <MemoizedCountTitle /> <CountDisplay /> <button onClick={() => setCount(count + 1)}>Increase</button> </>


Bây giờ, nếu bạn tăng số lượng và kiểm tra nhật ký, bạn sẽ có thể thấy rằng nó không hiển thị thành phần <CountTitle /> nữa.

API Redux và API ngữ cảnh

Redux là một thư viện quản lý trạng thái để quản lý trạng thái phức tạp với các chuyển đổi trạng thái dễ dự đoán hơn. Trong khi API bối cảnh được thiết kế để quản lý trạng thái đơn giản và truyền dữ liệu qua cây thành phần mà không cần khoan prop. Vậy khi nào nên chọn cái nào?


  • Sử dụng React Context API để quản lý trạng thái đơn giản, cục bộ trong đó trạng thái không thường xuyên thay đổi.


  • Sử dụng Redux cho các nhu cầu quản lý trạng thái phức tạp, đặc biệt là trong các ứng dụng lớn hơn, nơi lợi ích của việc quản lý trạng thái có cấu trúc vượt trội hơn so với thiết lập bổ sung.


Ngoài ra còn có một thư viện nữa cũng là một lựa chọn phổ biến cho việc quản lý state. Phản ứng giật lại .


  • React Recoil là một thư viện quản lý trạng thái dành cho React nhằm mục đích cung cấp sự đơn giản của API bối cảnh với sức mạnh và hiệu suất của Redux.


Nếu bạn muốn tìm hiểu thêm về React Recoil , hãy cho tôi biết trong phần nhận xét và tôi sẽ tạo các hướng dẫn chuyên sâu về chủ đề này dựa trên phản hồi của bạn.

Phần kết luận

API bối cảnh React.js cung cấp một cách mạnh mẽ và hiệu quả để quản lý trạng thái trên nhiều thành phần, giải quyết hiệu quả vấn đề khoan chống đỡ. Bằng cách sử dụng API ngữ cảnh, bạn có thể đơn giản hóa mã của mình, giảm việc hiển thị lại không cần thiết và cải thiện hiệu suất ứng dụng tổng thể.


Mặc dù API bối cảnh lý tưởng cho việc quản lý trạng thái đơn giản, nhưng các ứng dụng phức tạp hơn có thể được hưởng lợi từ việc sử dụng Redux hoặc các thư viện quản lý trạng thái khác như React Recoil . Hiểu thời điểm và cách sử dụng các công cụ này sẽ cho phép bạn xây dựng các ứng dụng React dễ bảo trì và mở rộng hơn.


Cảm ơn bạn đã đọc bài viết này, tôi hy vọng bạn thấy nó hữu ích. Nếu bạn quan tâm đến việc học và xây dựng các dự án bằng React, Redux và Next.js, bạn có thể truy cập kênh YouTube của tôi tại đây: CodeBucks


Dưới đây là các bài viết khác của tôi mà bạn có thể muốn đọc:

Ghé thăm blog cá nhân của tôi: DevDreaming