Hook chỉ đơn giản là các hàm cho phép bạn hook vào hoặc sử dụng các tính năng của React. Chúng được giới thiệu tại React Conf 2018 để giải quyết ba vấn đề chính của các thành phần lớp: wrapper hell, các thành phần lớn và các lớp gây nhầm lẫn. Hook cung cấp sức mạnh cho các thành phần chức năng của React, giúp có thể phát triển toàn bộ ứng dụng bằng hook.
Các vấn đề đã đề cập ở trên của các thành phần lớp được kết nối với nhau và việc giải quyết một trong hai vấn đề này có thể gây ra thêm nhiều vấn đề khác. Rất may là hook đã giải quyết tất cả các vấn đề một cách đơn giản và hiệu quả, đồng thời tạo không gian cho các tính năng thú vị hơn trong React. Hook không thay thế các khái niệm và lớp React đã tồn tại, chúng chỉ cung cấp một API để truy cập trực tiếp vào chúng.
Nhóm React đã giới thiệu một số hook trong React 16.8. Tuy nhiên, bạn cũng có thể sử dụng hook từ các nhà cung cấp bên thứ ba trong ứng dụng của mình hoặc thậm chí tạo một hook tùy chỉnh. Trong hướng dẫn này, chúng ta sẽ xem xét một số hook hữu ích trong React và cách sử dụng chúng. Chúng ta sẽ xem qua một số ví dụ mã của từng hook và cũng khám phá cách bạn tạo một hook tùy chỉnh.
Lưu ý: Hướng dẫn này yêu cầu bạn phải hiểu cơ bản về Javascript (ES6+) và React.
Hook dễ chia sẻ, bạn không cần phải sửa đổi các thành phần của mình trước khi sử dụng lại logic.
Một ví dụ điển hình về điều này là việc sử dụng
Trong đó
Trong khi ở thời đại Hooks, người ta có thể dễ dàng đạt được kết quả tương tự một cách gọn gàng và ngắn gọn bằng cách sử dụng các hook Redux
Hook sắp xếp các hiệu ứng phụ theo chức năng và có thể chia một thành phần thành các phần dựa trên chức năng.
React giải quyết vấn đề này bằng các thành phần chức năng và hook, cho phép các nhà phát triển tập trung vào dự án thay vì cú pháp mã.
Ví dụ, hai thành phần React sau sẽ mang lại kết quả chính xác như nhau.
Ví dụ đầu tiên là một thành phần dựa trên lớp trong khi ví dụ thứ hai là một thành phần chức năng. Mặc dù đây là một ví dụ đơn giản, hãy chú ý đến cách ví dụ đầu tiên được so sánh với ví dụ thứ hai.
Hook
Hook
Để khởi tạo trạng thái, chúng ta phải khai báo cả trạng thái và hàm cập nhật của nó và truyền một giá trị ban đầu.
Chúng ta có thể tự do gọi trạng thái và hàm cập nhật của mình theo bất kỳ cách nào chúng ta muốn nhưng theo quy ước, phần tử đầu tiên của mảng sẽ là trạng thái của chúng ta trong khi phần tử thứ hai sẽ là hàm cập nhật. Một cách làm phổ biến là thêm tiền tố cho hàm cập nhật của chúng ta là set theo sau là tên trạng thái của chúng ta theo dạng camel case.
Ví dụ, hãy đặt một trạng thái để giữ các giá trị count.
Lưu ý rằng giá trị ban đầu của trạng thái
Thiết lập trạng thái bằng
Trạng thái
Phương pháp này hoạt động khi chúng ta không muốn tham chiếu đến giá trị trạng thái trước đó. Nếu chúng ta muốn thực hiện điều đó, chúng ta cần truyền một hàm cho hàm
Giả sử chúng ta muốn thêm 5 vào biến
Trong đoạn mã trên, trước tiên chúng ta nhập hook
Toán tử lan truyền trong Javascript được sử dụng để tạo đối tượng mới từ đối tượng đã tồn tại. Điều này hữu ích ở đây vì
Hãy xem xét mã bên dưới để thiết lập trạng thái khi nhấp vào nút.
Trong đoạn mã trên, chúng ta đã tạo hai trạng thái
Chúng ta đã làm điều tương tự cho trình xử lý
Bản chất bất đồng bộ của
Như chúng ta đã thấy, chúng ta đặt trạng thái bằng
Các trạng thái được cập nhật không đồng bộ. Điều này có nghĩa là trạng thái mới trước tiên được thêm vào trạng thái đang chờ xử lý và sau đó, trạng thái được cập nhật. Vì vậy, bạn vẫn có thể nhận được giá trị trạng thái cũ nếu bạn truy cập trạng thái ngay khi nó được thiết lập.
Hãy xem xét ví dụ sau để quan sát hành vi này.
Xem thêm tại đây
Trong mã trên, chúng tôi đã tạo trạng thái
Nếu chúng ta muốn có trạng thái mới, chúng ta có thể xử lý theo cách tương tự như khi xử lý các hàm bất đồng bộ. Sau đây là một cách để thực hiện điều đó.
Xem thêm tại đây
Tại đây, chúng ta đã lưu trữ
Người dùng có thể quyết định khi nào
Hãy xem xét ví dụ bên dưới.
Trong mã trên, chúng tôi chỉ cần ghi
Đôi khi, chúng ta có thể muốn chạy hook một lần (trên mount) trong thành phần của mình. Chúng ta có thể thực hiện điều này bằng cách cung cấp tham số thứ hai cho hook
Hook
Bằng cách truyền một dấu ngoặc vuông rỗng vào tham số thứ hai của hook, chúng ta hướng dẫn React chạy hook
Chúng ta cũng có thể chạy hiệu ứng phụ của mình bất cứ khi nào một số giá trị phụ thuộc thay đổi. Điều này có thể được thực hiện bằng cách truyền các giá trị này vào danh sách các phụ thuộc.
Ví dụ, chúng ta có thể chạy
Sau khi chúng tôi cung cấp đối số thứ hai cho
Hook
Quy trình dọn dẹp thường tuân theo biểu mẫu bên dưới.
Mặc dù bạn có thể không tìm thấy nhiều trường hợp sử dụng cho đăng ký
Lấy và lấy lại dữ liệu bằng
Một trong những trường hợp sử dụng phổ biến nhất của hook
Để minh họa cho điều này, chúng tôi sẽ sử dụng dữ liệu người dùng giả mà tôi đã tạo từ
Trong đoạn mã trên, chúng ta đã tạo trạng thái
Lưu ý rằng chúng ta đã truyền một tham số rỗng vào hook. Điều này đảm bảo rằng nó chỉ được gọi một lần khi thành phần gắn kết.
Chúng ta cũng có thể lấy lại dữ liệu khi một số điều kiện thay đổi. Chúng ta sẽ hiển thị điều này trong đoạn mã bên dưới.
Ở đây chúng tôi đã tạo hai hook
Sau đó, chúng tôi tạo một
Tiếp theo, chúng tôi tạo các hàm để cập nhật giá trị của
Nếu muốn, chúng ta thậm chí có thể dọn dẹp hoặc hủy mã thông báo dựa trên lời hứa trong Axios, chúng ta có thể thực hiện điều đó bằng phương pháp dọn dẹp được thảo luận ở trên.
Tại đây, chúng ta đã truyền mã thông báo của Axios làm tham số thứ hai cho
Móc
Móc
Hook này lấy một reducer làm đối số và tùy chọn có thể lấy trạng thái ban đầu và một hàm init làm đối số.
Ở đây,
Hãy xem cách triển khai hook
[*] Ví dụ về TodoXem thêm tại đây
Trước hết, chúng ta nên tạo bộ giảm tốc để lưu trữ các trạng thái.
Chúng tôi đã tạo ba hằng số tương ứng với các loại hành động của mình. Chúng tôi có thể sử dụng trực tiếp các chuỗi nhưng phương pháp này được ưa chuộng hơn để tránh lỗi đánh máy.
Sau đó, chúng tôi đã tạo hàm reducer của mình. Giống như trong
Hơn nữa, đối với nhiều trường hợp sử dụng quản lý trạng thái,
Sau đó, chúng tôi đã sử dụng các câu lệnh
Đây là tệp
Tại đây, chúng tôi đã tạo một biểu mẫu chứa một phần tử đầu vào để thu thập thông tin đầu vào của người dùng và một nút để kích hoạt hành động. Khi biểu mẫu được gửi, chúng tôi đã phân phối một hành động thuộc loại
Tuy nhiên, phép thuật xảy ra vì chúng ta đang sử dụng hook
Để hiển thị các mục việc cần làm, chúng ta chỉ cần ánh xạ qua danh sách các việc cần làm được trả về trong đối tượng trạng thái của chúng ta như được hiển thị trong mã ở trên.
Điều này cho thấy sức mạnh của hook
Chúng ta cũng có thể truyền tham số thứ ba cho hook
Ví dụ, chúng ta có thể tạo hàm
và sau đó truyền nó vào hook
Nếu chúng ta làm điều này,
Hook
API React Context cung cấp một cách để chia sẻ trạng thái hoặc dữ liệu trong toàn bộ cây thành phần React. API đã có sẵn trong React, như một tính năng thử nghiệm, trong một thời gian nhưng nó đã trở nên an toàn để sử dụng trong React 16.3.0. API giúp chia sẻ dữ liệu giữa các thành phần dễ dàng trong khi loại bỏ prop drilling.
Mặc dù bạn có thể áp dụng React Context cho toàn bộ ứng dụng của mình, nhưng cũng có thể áp dụng cho một phần của ứng dụng.
Để sử dụng hook, trước tiên bạn cần tạo một ngữ cảnh bằng
Để chứng minh cách sử dụng hook
Chúng ta hãy tạo ngữ cảnh của mình trong tệp
Ở đây, chúng ta đã tạo một ngữ cảnh và truyền giá trị ban đầu là
Trong đoạn mã trên, chúng tôi đã bao bọc toàn bộ cây thành phần của mình bằng
Ví dụ, chúng ta đã truy cập ngữ cảnh trong
Tại đây, chúng tôi đã truy cập ngữ cảnh bằng cách sử dụng hook
Chủ đề hoặc các cấu hình cấp ứng dụng bậc cao khác là những ứng cử viên tốt cho ngữ cảnh.
Sử dụng
Khi được sử dụng với hook
Hãy cải thiện ứng dụng việc cần làm của chúng ta bằng API ngữ cảnh.
Như thường lệ, chúng ta cần tạo
Ở đây chúng ta đã tạo ngữ cảnh, truyền giá trị ban đầu của một mảng rỗng. Sau đó, chúng ta đã xuất ngữ cảnh.
Hãy cấu trúc lại tệp
Tại đây, chúng tôi đã gói tệp
Sau đó, chúng tôi đã tách màn hình hiển thị việc cần làm thành một thành phần
Bằng cách sử dụng giải cấu trúc mảng, chúng ta có thể truy cập trạng thái (rời khỏi hàm dispatch) từ ngữ cảnh bằng cách sử dụng hook
Chúng ta hãy xem xét thành phần
Một lần nữa sử dụng phân rã mảng, chúng ta đã truy cập hàm dispatch từ ngữ cảnh. Điều này cho phép chúng ta định nghĩa hàm
Cũng có thể lồng nhiều hơn một nhà cung cấp ngữ cảnh vào gốc của ứng dụng. Điều này có nghĩa là chúng ta có thể sử dụng nhiều ngữ cảnh để thực hiện các chức năng khác nhau trong một ứng dụng.
Để chứng minh điều này, hãy thêm chủ đề vào ví dụ việc cần làm.
Sau đây là những gì chúng ta sẽ xây dựng.
Xem thêm tại đây
Một lần nữa, chúng ta phải tạo
Tại đây, chúng ta đã tạo một ngữ cảnh và truyền
Trong mã trên, chúng ta đã tạo một đối tượng
Tiếp theo, chúng ta tạo
Giống như tất cả các bộ giảm tốc,
Sau khi thiết lập
Trong đoạn mã trên, chúng tôi đã thêm một vài thứ vào ứng dụng việc cần làm hiện có của mình. Chúng tôi bắt đầu bằng cách nhập
Sau đó, chúng ta lồng thành phần của mình với hàm cung cấp từ
Tuy nhiên, việc chuyển đổi chủ đề thực tế diễn ra trong thành phần
Trong thành phần này, chúng tôi đã sử dụng hook
Móc
Móc
Có thể sử dụng hook như sau:
Hãy xem xét ba trường hợp của hook
Trong ví dụ bên dưới, chúng ta sẽ tính toán PAYE và Thu nhập sau PAYE của nhân viên công ty với dữ liệu giả từ JSONPlaceholder.
Phép tính sẽ dựa trên quy trình tính thuế thu nhập cá nhân cho các nhà cung cấp tại Nigeria của PricewaterhouseCoopers có sẵn tại đây.
Điều này được hiển thị trong hộp cát bên dưới.
Xem thêm tại đây
Đầu tiên, chúng tôi truy vấn API để lấy dữ liệu của nhân viên. Chúng tôi cũng lấy dữ liệu cho từng nhân viên (theo id nhân viên của họ).
Chúng tôi đã sử dụng
Tiếp theo, sử dụng dữ liệu nhân viên mà chúng ta lấy được từ trên, hãy tính các biến cứu trợ:
Đây là một phép tính khá phức tạp nên chúng ta phải gói nó trong một hook
Hơn nữa, bằng cách sử dụng các giá trị giảm thuế thu được ở trên, chúng tôi muốn tính PAYE và thu nhập sau PAYE.
Chúng tôi đã thực hiện tính toán thuế (một phép tính khá phức tạp) bằng cách sử dụng các biến thuế đã tính toán ở trên và sau đó ghi nhớ bằng
Toàn bộ mã có sẵn trên tại đây.
Điều này tuân theo quy trình tính thuế được đưa ra tại đây. Đầu tiên, chúng tôi tính toán khoản giảm thuế khi xem xét thu nhập, số con và số người thân phụ thuộc. Sau đó, chúng tôi nhân thu nhập chịu thuế với tỷ lệ PIT theo từng bước. Mặc dù phép tính đang đề cập không hoàn toàn cần thiết cho hướng dẫn này, nhưng nó được cung cấp để cho chúng ta thấy lý do tại sao
Sau khi tính toán các giá trị, chúng ta chỉ cần hiển thị kết quả.
Lưu ý những điều sau về hook
Hook
Ví dụ, hãy xem xét các mã sau đây.
Trong ví dụ trên, cả
Sau đó, chúng ta có thể kích hoạt hàm gọi lại khi một hành động được thực hiện hoặc trong một hook
Trong đoạn mã trên, chúng tôi đã định nghĩa một hàm gọi lại bằng cách sử dụng hook
Cả
Lưu ý rằng các khái niệm, điều nên làm và không nên làm áp dụng cho hook
Hook
Nó phục vụ cùng một mục đích như
Ở đây chúng ta tạo một ref mới có tên là
Hook này chủ yếu được sử dụng cho hai mục đích:
Sau đây là một ví dụ rất đơn giản để chứng minh khái niệm này.
Trong ví dụ trên, chúng tôi đã định nghĩa
Để xem điều này, nếu chúng ta kiểm tra giá trị của
Ví dụ, chúng ta có thể làm cho văn bản in nghiêng khi thành phần được gắn kết.
Chúng ta thậm chí có thể thay đổi văn bản thành thứ khác.
Chúng ta thậm chí có thể thay đổi màu nền của vùng chứa cha.
Có thể thực hiện bất kỳ thao tác DOM nào ở đây. Lưu ý rằng
Một trường hợp sử dụng thú vị của hook
Trong đoạn mã trên, chúng tôi đã tạo
Các ref được tạo trong một thành phần cha có thể được đánh giá tại thành phần con bằng cách chuyển tiếp nó bằng
Trước tiên, hãy tạo một thành phần khác
Thành phần này chấp nhận
Sau đó, chúng ta có thể dễ dàng gọi thuộc tính này trong thành phần cha.
Ví dụ sau sẽ phát hiện số lần nhấp vào nút mà không cần hiển thị lại thành phần.
Trong đoạn mã trên, chúng ta đã tăng
Lưu ý rằng trong khi
Móc
Giống như móc
Chúng ta hãy xem một số ví dụ để chứng minh khái niệm này.
Trong đoạn mã trên, chúng ta đã tạo một trạng thái
Chúng tôi đã sử dụng
Các Hook
Mặt khác,
Sau khi kho lưu trữ Redux của chúng ta được kết nối với ứng dụng React thông qua nhà cung cấp Redux, chúng ta có thể gọi các hành động bằng
Lưu ý rằng các trạng thái này đi kèm với React Redux (một gói giúp đánh giá kho lưu trữ Redux dễ dàng trong ứng dụng React). Chúng không có sẵn trong thư viện Redux cốt lõi.
Các hook này rất dễ sử dụng. Trước tiên, chúng ta phải khai báo hàm dispatch và sau đó kích hoạt nó.
Trong đoạn mã trên, chúng ta đã import
Sau khi phân phối các hành động, các trạng thái của chúng ta sẽ khả dụng. Sau đó, chúng ta có thể truy xuất trạng thái bằng hook
Chúng ta hãy xem một ví dụ để chứng minh hai hook này.
Để chứng minh khái niệm này, chúng ta phải tạo một kho lưu trữ Redux, bộ giảm và các hành động. Để đơn giản hóa mọi thứ ở đây, chúng ta sẽ sử dụng thư viện Redux Toolkit với cơ sở dữ liệu giả của mình từ JSONPlaceholder.
Chúng ta cần cài đặt các gói sau để bắt đầu. Chạy các lệnh bash sau.
Đầu tiên, hãy tạo
Đây là thiết lập chuẩn cho bộ công cụ Redux. Chúng tôi đã sử dụng
Bộ công cụ Redux cũng giúp thiết lập cửa hàng dễ dàng. Đây là cửa hàng.
Ở đây, chúng tôi đã sử dụng
Chúng ta hãy tiến hành sử dụng hàm này trong ứng dụng của mình.
Trước tiên, chúng ta cần kết nối Redux với ứng dụng React của mình. Lý tưởng nhất là nên thực hiện việc này ở gốc ứng dụng. Tôi muốn thực hiện trong tệp
Ở đây, tôi đã nhập store mà tôi đã tạo ở trên và cả
Sau đó, tôi đã bọc toàn bộ ứng dụng bằng hàm
Sau đó, chúng ta có thể tiến hành sử dụng các hook
Hãy thực hiện điều này trong tệp
Trong đoạn mã trên, chúng tôi đã sử dụng hook
Hook
Điều hướng rất quan trọng trong ứng dụng React. Mặc dù bạn có thể thực hiện điều này theo một số cách, nhưng React Router cung cấp một cách đơn giản, hiệu quả và phổ biến để đạt được định tuyến động trong ứng dụng React. Hơn nữa, React Router cung cấp một vài hook để đánh giá trạng thái của router và thực hiện điều hướng trên trình duyệt nhưng để sử dụng chúng, trước tiên bạn cần thiết lập ứng dụng của mình đúng cách.
Để sử dụng bất kỳ hook React Router nào, trước tiên chúng ta nên bao bọc ứng dụng của mình bằng
Nhưng trước tiên, chúng ta phải cài đặt gói bằng cách chạy các lệnh sau.
Sau đó, chúng ta cần thiết lập ứng dụng của mình như sau. Tôi muốn thực hiện điều này trong tệp
Chúng ta có thể có nhiều Routes nhất có thể tùy thuộc vào số lượng thành phần mà chúng ta muốn hiển thị. Ở đây, chúng tôi chỉ render thành phần
Thứ tự rất quan trọng ở đây. Tuyến gốc phải được đặt bên dưới tuyến con, v.v. Để ghi đè thứ tự này, bạn cần đưa từ khóa
Bây giờ chúng ta đã thiết lập bộ định tuyến, sau đó chúng ta có thể sử dụng hook
Để sử dụng hook
Nếu chúng ta ghi lại lịch sử vào bảng điều khiển, chúng ta sẽ thấy một số thuộc tính được liên kết với nó. Những thuộc tính này bao gồm
Hãy sử dụng thuộc tính này để di chuyển từ trang này sang trang khác.
Giả sử chúng ta muốn tìm dữ liệu về một nhân viên cụ thể khi nhấp vào tên của họ. Chúng ta có thể sử dụng hook
Chúng ta có thể triển khai điều này trong tệp
Trong hàm
Hook
Hook này cũng đi kèm với React Router DOM. Đây là một hook rất phổ biến được sử dụng để làm việc với tham số chuỗi truy vấn. Hook này tương tự như
Hook
Thuộc tính
Sau đó, chúng ta có thể truy xuất các tham số tìm kiếm khác nhau bằng các gói như query-string hoặc bằng cách mã hóa chúng.
Móc
Nếu chúng ta thiết lập Tuyến đường của mình với tham số URL trong thuộc tính đường dẫn của nó, chúng ta có thể đánh giá các tham số đó dưới dạng cặp khóa/giá trị bằng móc
Ví dụ, giả sử rằng chúng ta có Tuyến đường sau.
Tuyến đường sẽ mong đợi một id động thay cho
Với hook
Ví dụ, giả sử người dùng truyền nội dung sau trong hàm với
Chúng ta có thể sử dụng hook
Nếu chúng ta ghi
Hook
Hook này cung cấp quyền truy cập vào đối tượng khớp. Nó trả về kết quả khớp gần nhất với một thành phần nếu không có đối số nào được cung cấp cho nó.
Đối tượng khớp trả về một số tham số bao gồm
Ví dụ, chúng ta có thể sử dụng
Trong đoạn mã trên, chúng ta đặt đường dẫn của tuyến đường với
Để điều này hoạt động, chúng ta vẫn cần thêm tuyến đường vào tệp
Custom hook cho phép chúng ta viết hàm một lần và sử dụng lại bất cứ khi nào cần và do đó tuân thủ nguyên tắc DRY.
Ví dụ, chúng ta có thể tạo một custom hook để lấy vị trí cuộn trên trang của mình như sau.
Ở đây, chúng tôi đã định nghĩa một hook tùy chỉnh để xác định vị trí cuộn trên một trang. Để đạt được điều này, trước tiên chúng tôi đã tạo một trạng thái,
Chúng ta có thể sử dụng hook tùy chỉnh này ở bất kỳ đâu trong ứng dụng của mình bằng cách gọi nó và sử dụng nó giống như chúng ta sử dụng bất kỳ trạng thái nào khác.
Tại đây, chúng ta đã nhập hook tùy chỉnh
Chúng ta có thể tạo các hook tùy chỉnh để thực hiện bất kỳ điều gì chúng ta có thể tưởng tượng trong ứng dụng của mình. Như bạn thấy, chúng ta chỉ cần sử dụng hook React tích hợp để thực hiện một số chức năng. Chúng ta cũng có thể sử dụng các thư viện của bên thứ ba để tạo các hook tùy chỉnh nhưng nếu làm như vậy, chúng ta sẽ phải cài đặt thư viện đó để có thể sử dụng hook.
Tôi khuyến khích bạn thử các hook này trong ứng dụng của riêng bạn để hiểu rõ hơn về chúng.
Các vấn đề đã đề cập ở trên của các thành phần lớp được kết nối với nhau và việc giải quyết một trong hai vấn đề này có thể gây ra thêm nhiều vấn đề khác. Rất may là hook đã giải quyết tất cả các vấn đề một cách đơn giản và hiệu quả, đồng thời tạo không gian cho các tính năng thú vị hơn trong React. Hook không thay thế các khái niệm và lớp React đã tồn tại, chúng chỉ cung cấp một API để truy cập trực tiếp vào chúng.
Nhóm React đã giới thiệu một số hook trong React 16.8. Tuy nhiên, bạn cũng có thể sử dụng hook từ các nhà cung cấp bên thứ ba trong ứng dụng của mình hoặc thậm chí tạo một hook tùy chỉnh. Trong hướng dẫn này, chúng ta sẽ xem xét một số hook hữu ích trong React và cách sử dụng chúng. Chúng ta sẽ xem qua một số ví dụ mã của từng hook và cũng khám phá cách bạn tạo một hook tùy chỉnh.
Lưu ý: Hướng dẫn này yêu cầu bạn phải hiểu cơ bản về Javascript (ES6+) và React.
Động lực đằng sau Hook
Như đã nêu trước đó, hook được tạo ra để giải quyết ba vấn đề: wrapper hell, các thành phần lớn và các lớp khó hiểu. Chúng ta hãy xem xét từng vấn đề này chi tiết hơn.Wrapper Hell
Các ứng dụng phức tạp được xây dựng bằng các thành phần lớp dễ gặp phải wrapper hell. Nếu bạn kiểm tra ứng dụng trong React Dev Tools, bạn sẽ thấy các thành phần được lồng sâu. Điều này khiến việc làm việc với các thành phần hoặc gỡ lỗi chúng trở nên rất khó khăn. Mặc dù những vấn đề này có thể được giải quyết bằng các thành phần bậc cao và các đạo cụ render, nhưng chúng yêu cầu bạn phải sửa đổi một chút mã của mình. Điều này có thể dẫn đến nhầm lẫn trong một ứng dụng phức tạp.Hook dễ chia sẻ, bạn không cần phải sửa đổi các thành phần của mình trước khi sử dụng lại logic.
Một ví dụ điển hình về điều này là việc sử dụng
kết nối
Thành phần bậc cao (HOC) Redux để đăng ký vào kho lưu trữ Redux. Giống như tất cả các HOC, để sử dụng kết nối HOC, bạn phải xuất thành phần cùng với các hàm bậc cao đã xác định. Trong trường hợp của connect
, chúng ta sẽ có một cái gì đó có dạng này.
Mã:
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)
mapStateToProps
và mapDispatchToProps
là các hàm cần được định nghĩa.Trong khi ở thời đại Hooks, người ta có thể dễ dàng đạt được kết quả tương tự một cách gọn gàng và ngắn gọn bằng cách sử dụng các hook Redux
useSelector
và useDispatch
.Huge Components
Các thành phần lớp thường chứa các hiệu ứng phụ và logic có trạng thái. Khi ứng dụng trở nên phức tạp hơn, thành phần thường trở nên lộn xộn và khó hiểu. Điều này là do các hiệu ứng phụ được mong đợi sẽ được sắp xếp theo các phương thức vòng đời thay vì theo chức năng. Mặc dù có thể chia nhỏ các thành phần và làm cho chúng đơn giản hơn, nhưng điều này thường dẫn đến mức độ trừu tượng cao hơn.Hook sắp xếp các hiệu ứng phụ theo chức năng và có thể chia một thành phần thành các phần dựa trên chức năng.
Các lớp gây nhầm lẫn
Các lớp thường là một khái niệm khó hơn so với các hàm. Các thành phần dựa trên lớp của React rất dài dòng và hơi khó đối với người mới bắt đầu. Nếu bạn mới làm quen với Javascript, bạn có thể thấy các hàm dễ bắt đầu hơn vì cú pháp nhẹ hơn so với các lớp. Cú pháp có thể gây nhầm lẫn; đôi khi, có thể quên liên kết trình xử lý sự kiện có thể làm hỏng mã.React giải quyết vấn đề này bằng các thành phần chức năng và hook, cho phép các nhà phát triển tập trung vào dự án thay vì cú pháp mã.
Ví dụ, hai thành phần React sau sẽ mang lại kết quả chính xác như nhau.
Mã:
import React, { Component } from "react";export default class App mở rộng Component { constructor(props) { super(props); this.state = { num: 0 }; this.incrementNumber = this.incrementNumber.bind(this); } incrementNumber() { this.setState({ num: this.state.num + 1 }); } render() { return ( [HEADING=1]{this.state.num}[/HEADING] Increment ); }}
Mã:
import React, { useState } from "react";export default function App() { const [num, setNum] = useState(0); function incrementNumber() { setNum(num + 1); } return ( [HEADING=1]{num}[/HEADING] Increment );}
Quy ước và quy tắc về Hooks
Trước khi đi sâu vào các hook khác nhau, có thể hữu ích khi xem qua quy ước và quy tắc áp dụng cho chúng. Sau đây là một số quy tắc áp dụng cho hooks.- Quy ước đặt tên của hooks phải bắt đầu bằng tiền tố
use
. Vì vậy, chúng ta có thể cóuseState
,useEffect
, v.v. Nếu bạn đang sử dụng trình soạn thảo mã hiện đại như Atom và VSCode, plugin ESLint có thể là một tính năng rất hữu ích cho hooks React. Plugin cung cấp các cảnh báo và gợi ý hữu ích về các phương pháp hay nhất. - Hooks phải được gọi ở cấp cao nhất của một thành phần, trước câu lệnh return. Chúng không thể được gọi bên trong một câu lệnh điều kiện, vòng lặp hoặc các hàm lồng nhau.
- Các hook phải được gọi từ một hàm React (bên trong một thành phần React hoặc một hook khác). Không nên gọi từ một hàm Vanilla JS.
Hook useState
Hook useState
là hook React cơ bản và hữu ích nhất. Giống như các hook tích hợp khác, hook này phải được import từ react
để sử dụng trong ứng dụng của chúng ta.
Mã:
import {useState} from 'react'
Mã:
const [state, updaterFn] = useState('')
Ví dụ, hãy đặt một trạng thái để giữ các giá trị count.
Mã:
const [count, setCount] = useState(0)
count
của chúng ta được đặt thành 0
chứ không phải là một chuỗi rỗng. Nói cách khác, chúng ta có thể khởi tạo trạng thái của mình thành bất kỳ loại biến JavaScript nào, cụ thể là số, chuỗi, boolean, mảng, đối tượng và thậm chí là BigInt. Có một sự khác biệt rõ ràng giữa việc đặt trạng thái bằng hook useState
và trạng thái thành phần dựa trên lớp. Điều đáng chú ý là hook useState
trả về một mảng, còn được gọi là biến trạng thái và trong ví dụ trên, chúng tôi đã giải cấu trúc mảng thành state
và hàm updater
.Kết xuất lại các thành phần
Đặt trạng thái bằng hookuseState
khiến thành phần tương ứng kết xuất lại. Tuy nhiên, điều này chỉ xảy ra nếu React phát hiện ra sự khác biệt giữa trạng thái trước đó hoặc cũ và trạng thái mới. React thực hiện so sánh trạng thái bằng cách sử dụng thuật toán JavaScript.Thiết lập trạng thái bằng useState
Trạng thái count
của chúng ta có thể được thiết lập thành các giá trị trạng thái mới chỉ bằng cách truyền giá trị mới cho hàm cập nhật setCount
như sau setCount(newValue)
.Phương pháp này hoạt động khi chúng ta không muốn tham chiếu đến giá trị trạng thái trước đó. Nếu chúng ta muốn thực hiện điều đó, chúng ta cần truyền một hàm cho hàm
setCount
.Giả sử chúng ta muốn thêm 5 vào biến
count
của mình bất cứ khi nào một nút được nhấp, chúng ta có thể thực hiện như sau.
Mã:
import {useState} from 'react'const CountExample = () => { // khởi tạo trạng thái đếm của chúng ta const [count, setCount] = useState(0) // thêm 5 vào trạng thái đếm trước đó const handleClick = () =>{ setCount(prevCount => prevCount + 5) } return( [HEADING=1]{count} [/HEADING] Thêm Năm )}export default CountExample
useState
từ react
rồi khởi tạo trạng thái count
với giá trị mặc định là 0. Chúng ta đã tạo trình xử lý onClick
để tăng giá trị của count
thêm 5 bất cứ khi nào nút được nhấp. Sau đó chúng tôi hiển thị kết quả trong thẻ h1
.Thiết lập Mảng và Trạng thái Đối tượng
Trạng thái cho mảng và đối tượng có thể được thiết lập theo cách tương tự như các kiểu dữ liệu khác. Tuy nhiên, nếu chúng ta muốn giữ nguyên các giá trị đã tồn tại, chúng ta cần sử dụng toán tử lan truyền ES6 khi thiết lập trạng thái.Toán tử lan truyền trong Javascript được sử dụng để tạo đối tượng mới từ đối tượng đã tồn tại. Điều này hữu ích ở đây vì
React
so sánh các trạng thái với hoạt động Object.is
và sau đó hiển thị lại cho phù hợp.Hãy xem xét mã bên dưới để thiết lập trạng thái khi nhấp vào nút.
Mã:
import {useState} from 'react'const StateExample = () => { //khởi tạo mảng và trạng thái đối tượng của chúng ta const [arr, setArr] = useState([2, 4]) const [obj, setObj] = useState({num: 1, name: 'Desmond'}) // đặt arr thành các giá trị mảng mới const handleArrClick = () =>{ const newArr = [1, 5, 7] setArr([...arr, ...newArr]) } // đặt obj thành các giá trị đối tượng mới const handleObjClick = () =>{ const newObj = {name: 'Ifeanyi', age: 25} setObj({...obj, ...newObj}) } return( Đặt trạng thái mảng Set Object State )}export default StateExample
arr
và obj
, và khởi tạo chúng thành một số giá trị mảng và đối tượng tương ứng. Sau đó, chúng ta đã tạo các trình xử lý onClick
có tên là handleArrClick
và handleObjClick
để đặt các trạng thái của mảng và đối tượng tương ứng. Khi handleArrClick
kích hoạt, chúng ta gọi setArr
và sử dụng toán tử lan truyền ES6 để lan truyền các giá trị mảng đã tồn tại và thêm newArr
vào đó.Chúng ta đã làm điều tương tự cho trình xử lý
handleObjClick
. Ở đây chúng ta gọi setObj
, phân tán các giá trị đối tượng hiện có bằng toán tử phân tán ES6 và cập nhật các giá trị của name
và age
.Bản chất bất đồng bộ của useState
Như chúng ta đã thấy, chúng ta đặt trạng thái bằng useState
bằng cách truyền một giá trị mới cho hàm cập nhật. Nếu hàm cập nhật được gọi nhiều lần, các giá trị mới sẽ được thêm vào hàng đợi và việc kết xuất lại được thực hiện theo đó bằng cách sử dụng phép so sánh Object.is
của JavaScript.Các trạng thái được cập nhật không đồng bộ. Điều này có nghĩa là trạng thái mới trước tiên được thêm vào trạng thái đang chờ xử lý và sau đó, trạng thái được cập nhật. Vì vậy, bạn vẫn có thể nhận được giá trị trạng thái cũ nếu bạn truy cập trạng thái ngay khi nó được thiết lập.
Hãy xem xét ví dụ sau để quan sát hành vi này.
Xem thêm tại đây
Trong mã trên, chúng tôi đã tạo trạng thái
count
bằng cách sử dụng hook useState
. Sau đó, chúng tôi đã tạo trình xử lý onClick
để tăng trạng thái count
bất cứ khi nào nút được nhấp.Lưu ý rằng mặc dù trạng thái count
tăng lên, như được hiển thị trong thẻ h2
, trạng thái trước đó vẫn được ghi lại trong bảng điều khiển. Điều này là do bản chất bất đồng bộ của hook.Nếu chúng ta muốn có trạng thái mới, chúng ta có thể xử lý theo cách tương tự như khi xử lý các hàm bất đồng bộ. Sau đây là một cách để thực hiện điều đó.
Xem thêm tại đây
Tại đây, chúng ta đã lưu trữ
newCountValue
đã tạo để lưu trữ giá trị đếm đã cập nhật và sau đó đặt trạng thái count
với giá trị đã cập nhật. Sau đó, chúng ta đã ghi lại giá trị đếm đã cập nhật trong bảng điều khiển.useEffect
Hook
useEffect
là một hook React quan trọng khác được sử dụng trong hầu hết các dự án. Nó thực hiện một điều tương tự như các phương thức vòng đời componentDidMount
, componentWillUnmount
và componentDidUpdate
của thành phần dựa trên lớp. useEffect
cung cấp cho chúng ta cơ hội để viết các mã bắt buộc có thể có tác dụng phụ đối với ứng dụng. Ví dụ về các hiệu ứng như vậy bao gồm ghi nhật ký, đăng ký, đột biến, v.v.Người dùng có thể quyết định khi nào
useEffect
sẽ chạy, tuy nhiên, nếu không thiết lập, các hiệu ứng phụ sẽ chạy trên mỗi lần kết xuất hoặc kết xuất lại.Hãy xem xét ví dụ bên dưới.
Mã:
import {useState, useEffect} from 'react'const App = () =>{ const [count, setCount] = useState(0) useEffect(() =>{ console.log(count) }) return( ... )}
count
vào useEffect
. Mã này sẽ chạy sau mỗi lần kết xuất thành phần.Đôi khi, chúng ta có thể muốn chạy hook một lần (trên mount) trong thành phần của mình. Chúng ta có thể thực hiện điều này bằng cách cung cấp tham số thứ hai cho hook
useEffect
.
Mã:
import {useState, useEffect} from 'react'const App = () =>{ const [count, setCount] = useState(0) useEffect(() =>{ setCount(count + 1) }, []) return( [HEADING=1]{count}[/HEADING] ... )}
useEffect
có hai tham số, tham số đầu tiên là hàm chúng ta muốn chạy trong khi tham số thứ hai là một mảng các phụ thuộc. Nếu tham số thứ hai không được cung cấp, hook sẽ chạy liên tục.Bằng cách truyền một dấu ngoặc vuông rỗng vào tham số thứ hai của hook, chúng ta hướng dẫn React chạy hook
useEffect
chỉ một lần, khi gắn kết. Điều này sẽ hiển thị giá trị 1
trong thẻ h1
vì số đếm sẽ được cập nhật một lần, từ 0 đến 1, khi thành phần gắn kết.Chúng ta cũng có thể chạy hiệu ứng phụ của mình bất cứ khi nào một số giá trị phụ thuộc thay đổi. Điều này có thể được thực hiện bằng cách truyền các giá trị này vào danh sách các phụ thuộc.
Ví dụ, chúng ta có thể chạy
useEffect
bất cứ khi nào count
thay đổi như sau.
Mã:
import { useState, useEffect } from "react";const App = () => { const [count, setCount] = useState(0); useEffect(() => { console.log(count); }, [count]); return ( setCount(count + 1)}>Tăng );};export default App;
useEffect
ở trên sẽ chạy khi một trong hai điều kiện này được đáp ứng.- Khi gắn kết — sau khi thành phần được hiển thị.
- Khi giá trị của
count
thay đổi.
console.log
sẽ chạy và ghi count
thành 0. Sau khi count
được cập nhật, điều kiện thứ hai được đáp ứng, do đó useEffect
chạy lại, điều này sẽ tiếp tục bất cứ khi nào nút được nhấp.Sau khi chúng tôi cung cấp đối số thứ hai cho
useEffect
, chúng tôi mong đợi sẽ truyền tất cả các phụ thuộc vào nó. Nếu bạn đã cài đặt ESLINT
, nó sẽ hiển thị lỗi lint nếu bất kỳ phụ thuộc nào không được truyền vào danh sách tham số. Điều này cũng có thể khiến hiệu ứng phụ hoạt động không mong muốn, đặc biệt là nếu nó phụ thuộc vào các tham số không được truyền.Dọn dẹp hiệu ứng
useEffect
cũng cho phép chúng ta dọn dẹp tài nguyên trước khi thành phần được hủy gắn kết. Điều này có thể cần thiết để ngăn rò rỉ bộ nhớ và làm cho ứng dụng hiệu quả hơn. Để thực hiện điều này, chúng ta sẽ trả về hàm dọn dẹp ở cuối hook.
Mã:
useEffect(() => { console.log('mounted') return () => console.log('unmounting... clean up here')})
useEffect
ở trên sẽ ghi lại mounted
khi thành phần được gắn kết. Unmounting… clean up here sẽ được ghi lại khi thành phần được hủy gắn kết. Điều này có thể xảy ra khi thành phần bị xóa khỏi UI.Quy trình dọn dẹp thường tuân theo biểu mẫu bên dưới.
Mã:
useEffect(() => { //Hiệu ứng chúng ta muốn tạo effect //Sau đó, chúng ta trả về việc dọn dẹp return () => việc dọn dẹp/hủy đăng ký})
useEffect
, nhưng nó hữu ích khi xử lý đăng ký và bộ hẹn giờ. Đặc biệt, khi xử lý web socket, bạn có thể cần hủy đăng ký khỏi mạng để tiết kiệm tài nguyên và cải thiện hiệu suất khi thành phần hủy gắn kết.Lấy và lấy lại dữ liệu bằng useEffect
Một trong những trường hợp sử dụng phổ biến nhất của hook useEffect
là lấy và lấy trước dữ liệu từ API.Để minh họa cho điều này, chúng tôi sẽ sử dụng dữ liệu người dùng giả mà tôi đã tạo từ
JSONPlaceholder
để lấy dữ liệu bằng hook useEffect
.
Mã:
import { useEffect, useState } from "react";import axios from "axios";export default function App() { const [users, setUsers] = useState([]); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/users"; useEffect(() => { const fetchUsers = async () => { const { data } = await axios.get(endPoint); setUsers(data); }; fetchUsers(); }, []); return ( {users.map((user) => ( [HEADING=2]{user.name}[/HEADING]
Nghề nghiệp: {user.job}
Giới tính: {user.sex}
))} );}
users
bằng cách sử dụng hook useState
. Sau đó, chúng ta lấy dữ liệu từ API bằng Axios. Đây là một quy trình không đồng bộ, vì vậy chúng ta đã sử dụng hàm async/await, chúng ta cũng có thể sử dụng dấu chấm rồi cú pháp. Vì chúng ta đã lấy danh sách người dùng, nên chúng ta chỉ cần ánh xạ qua nó để hiển thị dữ liệu.Lưu ý rằng chúng ta đã truyền một tham số rỗng vào hook. Điều này đảm bảo rằng nó chỉ được gọi một lần khi thành phần gắn kết.
Chúng ta cũng có thể lấy lại dữ liệu khi một số điều kiện thay đổi. Chúng ta sẽ hiển thị điều này trong đoạn mã bên dưới.
Mã:
import { useEffect, useState } from "react";import axios from "axios";export default function App() { const [userIDs, setUserIDs] = useState([]); const [người dùng, setUser] = useState({}); const [currentID, setCurrentID] = useState(1); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/userdata/users"; useEffect(() => { axios.get(endPoint).then(({ data }) => setUserIDs(data)); }, []); useEffect(() => { const fetchUserIDs = async () => { const { data } = await axios.get(`${endPoint}/${currentID}`}); setUser(data); }; fetchUserIDs(); }, [currentID]); const moveToNextUser = () => { setCurrentID((prevId) => (prevId < userIDs.length ? prevId + 1 : prevId)); }; const moveToPrevUser = () => { setCurrentID((prevId) => (prevId === 1 ? prevId : prevId - 1)); }; return ( [HEADING=2]{user.name}[/HEADING]
Nghề nghiệp: {user.job}
Giới tính: {user.sex}
Prev Next );}
useEffect
. Trong hook đầu tiên, chúng tôi đã sử dụng cú pháp dot then để lấy tất cả người dùng từ API của chúng tôi. Điều này là cần thiết để xác định số lượng người dùng.Sau đó, chúng tôi tạo một
useEffect
khác để lấy người dùng dựa trên id
. useEffect
này sẽ lấy lại dữ liệu bất cứ khi nào id thay đổi. Để đảm bảo điều này, chúng tôi đã truyền id
vào danh sách phụ thuộc.Tiếp theo, chúng tôi tạo các hàm để cập nhật giá trị của
id
bất cứ khi nào các nút được nhấp. Khi giá trị của id
thay đổi, useEffect
sẽ chạy lại và lấy lại dữ liệu.Nếu muốn, chúng ta thậm chí có thể dọn dẹp hoặc hủy mã thông báo dựa trên lời hứa trong Axios, chúng ta có thể thực hiện điều đó bằng phương pháp dọn dẹp được thảo luận ở trên.
Mã:
useEffect(() => { const source = axios.CancelToken.source(); const fetchUsers = async () => { const { data } = await axios.get(`${endPoint}/${num}`, { cancelToken: source.token }); setUser(data); }; fetchUsers(); return () => source.cancel(); }, [num]);
axios.get
. Khi thành phần hủy gắn kết, chúng ta sẽ hủy đăng ký bằng cách gọi phương thức hủy của đối tượng nguồn.Móc useReducer
Móc useReducer
là một móc React rất hữu ích có chức năng tương tự như móc useState
. Theo tài liệu React, móc này nên được sử dụng để xử lý logic phức tạp hơn móc useState
. Điều đáng chú ý là hook useState
được triển khai nội bộ với hook useReducer.Hook này lấy một reducer làm đối số và tùy chọn có thể lấy trạng thái ban đầu và một hàm init làm đối số.
Mã:
const [state, dispatch] = useReducer(reducer, initialState, init)
init
là một hàm và nó được sử dụng bất cứ khi nào chúng ta muốn tạo trạng thái ban đầu một cách lười biếng.Hãy xem cách triển khai hook
useReducer
bằng cách tạo một ứng dụng việc cần làm đơn giản như được hiển thị trong hộp cát bên dưới.[*] Ví dụ về TodoXem thêm tại đây
Trước hết, chúng ta nên tạo bộ giảm tốc để lưu trữ các trạng thái.
Mã:
export const ADD_TODO = "ADD_TODO";export const REMOVE_TODO = "REMOVE_TODO";export const COMPLETE_TODO = "COMPLETE_TODO";const reducer = (trạng thái, hành động) => { switch (action.type) { case ADD_TODO: const newTodo = { id: action.id, text: action.text, completed: false }; return [...state, newTodo]; case REMOVE_TODO: return state.filter((todo) => todo.id !== action.id); case COMPLETE_TODO: const completeTodo = state.map((todo) => { if (todo.id === action.id) { return { ...todo, completed: !todo.completed }; } else { return todo; } }); return completeTodo; default: return state; }};export default reducer;
Sau đó, chúng tôi đã tạo hàm reducer của mình. Giống như trong
Redux
, reducer phải lấy trạng thái và đối tượng hành động. Nhưng không giống như Redux, chúng tôi không cần khởi tạo reducer của mình tại đây.Hơn nữa, đối với nhiều trường hợp sử dụng quản lý trạng thái,
useReducer
cùng với dispatch
được hiển thị qua ngữ cảnh có thể cho phép một ứng dụng lớn hơn kích hoạt các hành động, cập nhật state
và lắng nghe nó.Sau đó, chúng tôi đã sử dụng các câu lệnh
switch
để kiểm tra loại hành động do người dùng truyền. Nếu loại hành động là ADD_TODO
, chúng ta muốn truyền một việc cần làm mới và nếu là REMOVE_TODO
, chúng ta muốn lọc các việc cần làm và xóa việc cần làm tương ứng với id
do người dùng truyền. Nếu là COMPLETE_TODO
, chúng ta muốn ánh xạ qua các việc cần làm và chuyển đổi việc cần làm có id
do người dùng truyền.Đây là tệp
App.js
nơi chúng ta triển khai reducer
.
Mã:
import { useReducer, useState } from "react";import "./styles.css";import reducer, { ADD_TODO, REMOVE_TODO, COMPLETE_TODO } from "./reducer";export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = [ { id: id, text: "First Item", completed: false } ]; //Chúng ta cũng có thể truyền một mảng rỗng làm trạng thái ban đầu //const initialState = [] const [state, dispatch] = useReducer(reducer, initialState); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); dispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; const removeTodo = (id) => { dispatch({ type: REMOVE_TODO, id }); }; const completeTodo = (id) => { dispatch({ type: COMPLETE_TODO, id }); }; trả về ( [HEADING=1]Ví dụ về Todo[/HEADING] setText(e.target.value)} /> + {state.map((todo) => (
{todo.text}
removeTodo(todo.id)}>✕ completeTodo(todo.id)}>✓ ))} );}
ADD_TODO
, truyền một id mới và văn bản việc cần làm. Chúng tôi đã tạo một id mới bằng cách tăng giá trị id trước đó lên 1. Sau đó, chúng tôi xóa hộp văn bản đầu vào. Để xóa và hoàn thành việc cần làm, chúng tôi chỉ cần phân phối các hành động thích hợp. Những điều này đã được triển khai trong bộ giảm như được hiển thị ở trên.Tuy nhiên, phép thuật xảy ra vì chúng ta đang sử dụng hook
useReducer
. Hook này chấp nhận bộ giảm và trạng thái ban đầu và trả về trạng thái và hàm dispatch. Ở đây, hàm dispatch có cùng mục đích như hàm setter cho hook useState
và chúng ta có thể gọi nó bất cứ tên nào chúng ta muốn thay vì dispatch
.Để hiển thị các mục việc cần làm, chúng ta chỉ cần ánh xạ qua danh sách các việc cần làm được trả về trong đối tượng trạng thái của chúng ta như được hiển thị trong mã ở trên.
Điều này cho thấy sức mạnh của hook
useReducer
. Chúng ta cũng có thể đạt được chức năng này bằng hook useState
nhưng như bạn có thể thấy từ ví dụ trên, hook useReducer
đã giúp chúng ta giữ mọi thứ gọn gàng hơn. useReducer
thường có lợi khi đối tượng trạng thái là một cấu trúc phức tạp và được cập nhật theo nhiều cách khác nhau so với việc thay thế giá trị đơn giản. Ngoài ra, khi các hàm cập nhật này trở nên phức tạp hơn, useReducer
giúp dễ dàng lưu trữ toàn bộ sự phức tạp đó trong một hàm giảm (là một hàm JS thuần túy), giúp việc viết các bài kiểm tra chỉ dành cho hàm giảm trở nên rất dễ dàng.Chúng ta cũng có thể truyền tham số thứ ba cho hook
useReducer
để tạo trạng thái ban đầu một cách lười biếng. Điều này có nghĩa là chúng ta có thể tính toán trạng thái ban đầu trong hàm init
.Ví dụ, chúng ta có thể tạo hàm
init
như sau:
Mã:
const initFunc = () => [ { id: id, text: "First Item", completed: false }]
useReducer
của chúng ta.
Mã:
const [state, dispatch] = useReducer(reducer, initialState, initFunc)
initFunc
sẽ ghi đè initialState
mà chúng ta đã cung cấp và trạng thái ban đầu sẽ được tính toán một cách lười biếng.Hook useContext
API React Context cung cấp một cách để chia sẻ trạng thái hoặc dữ liệu trong toàn bộ cây thành phần React. API đã có sẵn trong React, như một tính năng thử nghiệm, trong một thời gian nhưng nó đã trở nên an toàn để sử dụng trong React 16.3.0. API giúp chia sẻ dữ liệu giữa các thành phần dễ dàng trong khi loại bỏ prop drilling.Mặc dù bạn có thể áp dụng React Context cho toàn bộ ứng dụng của mình, nhưng cũng có thể áp dụng cho một phần của ứng dụng.
Để sử dụng hook, trước tiên bạn cần tạo một ngữ cảnh bằng
React.createContext
và sau đó ngữ cảnh này có thể được truyền vào hook.Để chứng minh cách sử dụng hook
useContext
, chúng ta hãy tạo một ứng dụng đơn giản sẽ tăng kích thước phông chữ trong toàn bộ ứng dụng của chúng ta.Chúng ta hãy tạo ngữ cảnh của mình trong tệp
context.js
.
Mã:
import { createContext } from "react";//Ở đây, chúng ta đặt fontSize ban đầu là 16.const fontSizeContext = createContext(16);export default fontSizeContext;
16
vào ngữ cảnh đó, sau đó xuất ngữ cảnh. Tiếp theo, hãy kết nối ngữ cảnh của chúng ta với ứng dụng của chúng ta.
Mã:
import FontSizeContext from "./context";import { useState } from "react";import PageOne from "./PageOne";import PageTwo from "./PageTwo";const App = () => { const [size, setSize] = useState(16); return (
setSize(size + 5)}>Tăng font setSize((prevSize) => Math.min(11, prevSize - 5)) } > Giảm font );};export default App;
FontSizeContext.Provider
và truyền size
vào giá trị prop của nó. Ở đây, size
là trạng thái được tạo bằng hook useState
. Điều này cho phép chúng ta thay đổi giá trị prop bất cứ khi nào trạng thái size
thay đổi. Bằng cách bao bọc toàn bộ thành phần bằng Provider
, chúng ta có thể truy cập ngữ cảnh ở bất kỳ đâu trong ứng dụng của mình.Ví dụ, chúng ta đã truy cập ngữ cảnh trong
và
. Do đó, kích thước phông chữ sẽ tăng lên trên cả hai thành phần này khi chúng ta tăng kích thước phông chữ từ tệp App.js
. Chúng ta có thể tăng hoặc giảm kích thước phông chữ từ các nút như được hiển thị ở trên và sau khi thực hiện, kích thước phông chữ sẽ thay đổi trong toàn bộ ứng dụng.
Mã:
import { useContext } from "react";import context from "./context";const PageOne = () => { const size = useContext(context); return
Nội dung từ trang đầu tiên
;};export default PageOne;
useContext
từ thành phần PageOne
của chúng tôi. Sau đó, chúng tôi đã sử dụng ngữ cảnh này để đặt thuộc tính font-size của mình. Một quy trình tương tự áp dụng cho tệp PageTwo.js
.Chủ đề hoặc các cấu hình cấp ứng dụng bậc cao khác là những ứng cử viên tốt cho ngữ cảnh.
Sử dụng useContext
và useReducer
Khi được sử dụng với hook useReducer
, useContext
cho phép chúng tôi tạo hệ thống quản lý trạng thái của riêng mình. Chúng ta có thể tạo các trạng thái toàn cục và dễ dàng quản lý chúng trong ứng dụng của mình.Hãy cải thiện ứng dụng việc cần làm của chúng ta bằng API ngữ cảnh.
Như thường lệ, chúng ta cần tạo
todoContext
trong tệp todoContext.js
.
Mã:
import { createContext } from "react";const initialState = [];export default createContext(initialState);
Hãy cấu trúc lại tệp
App.js
của chúng ta bằng cách tách danh sách việc cần làm và các mục.
Mã:
import { useReducer, useState } từ "react";import "./styles.css";import todoReducer, { ADD_TODO } từ "./todoReducer";import TodoContext từ "./todoContext";import TodoList từ "./TodoList";export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = []; const [todoState, todoDispatch] = useReducer(todoReducer, initialState); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); todoDispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; return ( [HEADING=1]Todo Example[/HEADING] setText(e.target.value)} /> + );}
App.js
của mình bằng TodoContext.Provider
rồi truyền các giá trị trả về của todoReducer
vào đó. Điều này giúp trạng thái của trình giảm và hàm dispatch
có thể truy cập được trong toàn bộ ứng dụng của chúng tôi.Sau đó, chúng tôi đã tách màn hình hiển thị việc cần làm thành một thành phần
TodoList
. Chúng tôi đã thực hiện việc này mà không cần prop drilling, nhờ có Context API. Hãy cùng xem tệp TodoList.js
.
Mã:
import React, { useContext } from "react";import TodoContext from "./todoContext";import Todo from "./Todo";const TodoList = () => { const [state] = useContext(TodoContext); return ( {state.map((todo) => ( ))} );};export default TodoList;
useContext
. Sau đó, chúng ta có thể ánh xạ qua trạng thái và hiển thị các mục việc cần làm. Chúng ta vẫn trích xuất điều này trong một thành phần Todo
. Hàm map ES6+ yêu cầu chúng ta phải truyền một khóa duy nhất và vì chúng ta cần việc cần làm cụ thể, chúng ta cũng truyền nó cùng.Chúng ta hãy xem xét thành phần
Todo
.
Mã:
import React, { useContext } from "react";nhập TodoContext từ "./todoContext";nhập { REMOVE_TODO, COMPLETE_TODO } từ "./todoReducer";const Todo = ({ todo }) => { const [, dispatch] = useContext(TodoContext); const removeTodo = (id) => { dispatch({ loại: REMOVE_TODO, id }); }; const completeTodo = (id) => { dispatch({ loại: COMPLETE_TODO, id }); }; return (
{todo.text}
removeTodo(todo.id)}>✕ completeTodo(todo.id)}>✓ );};export default Todo;
completeTodo
và removeTodo
như đã thảo luận trong phần useReducer
. Với prop todo
được truyền từ todoList.js
, chúng ta có thể hiển thị một mục việc cần làm. Chúng ta cũng có thể đánh dấu mục đó là đã hoàn thành và xóa mục việc cần làm khi chúng ta thấy phù hợp.Cũng có thể lồng nhiều hơn một nhà cung cấp ngữ cảnh vào gốc của ứng dụng. Điều này có nghĩa là chúng ta có thể sử dụng nhiều ngữ cảnh để thực hiện các chức năng khác nhau trong một ứng dụng.
Để chứng minh điều này, hãy thêm chủ đề vào ví dụ việc cần làm.
Sau đây là những gì chúng ta sẽ xây dựng.
Xem thêm tại đây
Một lần nữa, chúng ta phải tạo
themeContext
. Để thực hiện việc này, hãy tạo tệp themeContext.js
và thêm các mã sau.
Mã:
import { createContext } from "react";import colors from "./colors";export default createContext(colors.light);
colors.light
làm giá trị ban đầu. Hãy định nghĩa các màu bằng thuộc tính này trong tệp colors.js
.
Mã:
const colors = { light: { backgroundColor: "#fff", color: "#000" }, dark: { backgroundColor: "#000", color: "#fff" }};export default colors;
colors
chứa các thuộc tính sáng và tối. Mỗi thuộc tính có đối tượng backgroundColor
và color
.Tiếp theo, chúng ta tạo
themeReducer
để xử lý trạng thái chủ đề.
Mã:
import Colors from "./colors";export const LIGHT = "LIGHT";export const DARK = "DARK";const themeReducer = (state, action) => { switch (action.type) { case LIGHT: return { ...Colors.light }; case DARK: return { ...Colors.dark }; default: return state; }};export default themeReducer;
themeReducer
lấy trạng thái và hành động. Sau đó, nó sử dụng câu lệnh switch
để xác định hành động hiện tại. Nếu nó thuộc loại LIGHT
, chúng ta chỉ cần gán các prop Colors.light
và nếu nó thuộc loại DARK
, chúng ta sẽ hiển thị các prop Colors.dark
. Chúng ta có thể dễ dàng thực hiện điều này bằng hook useState
nhưng chúng ta chọn useReducer
để làm rõ vấn đề.Sau khi thiết lập
themeReducer
, chúng ta có thể tích hợp nó vào tệp App.js
của mình.
Mã:
import { useReducer, useState, useCallback } from "react";import "./styles.css";import todoReducer, { ADD_TODO } from "./todoReducer";import TodoContext from "./todoContext";import ThemeContext from "./themeContext";import TodoList từ "./TodoList";import themeReducer, { DARK, LIGHT } từ "./themeReducer";import Colors từ "./colors";import ThemeToggler từ "./ThemeToggler";const themeSetter = useCallback( theme => themeDispatch({type: theme}, [themeDispatch]);export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = []; const [todoState, todoDispatch] = useReducer(todoReducer, initialState); const [themeState, themeDispatch] = useReducer(themeReducer, Colors.light); const themeSetter = useCallback((theme) => { themeDispatch({ type: theme }); }, [themeDispatch] ); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); todoDispatch({ type: ADD_TODO, id: newId, văn bản: văn bản }); setText(""); }; trả về ( [HEADING=1]Ví dụ Todo[/HEADING] setText(e.target.value)} /> + );}
ThemeContext
, themeReducer
, ThemeToggler
và Colors
. Chúng tôi đã tạo một bộ giảm tốc bằng cách sử dụng móc useReducer
, truyền themeReducer
và giá trị ban đầu là Colors.light
vào đó. Điều này trả về themeState
và themeDispatch
cho chúng ta.Sau đó, chúng ta lồng thành phần của mình với hàm cung cấp từ
ThemeContext
, truyền các hàm themeState
và dispatch
cho nó. Chúng ta cũng đã thêm các kiểu chủ đề vào đó bằng cách trải rộng các themeStates
. Điều này hoạt động vì đối tượng colors
đã xác định các thuộc tính tương tự như những gì các kiểu JSX sẽ chấp nhận.Tuy nhiên, việc chuyển đổi chủ đề thực tế diễn ra trong thành phần
ThemeToggler
. Chúng ta hãy cùng xem xét nó.
Mã:
import ThemeContext from "./themeContext";import { useContext, useState } from "react";import { DARK, LIGHT } from "./themeReducer";const ThemeToggler = () => { const [showLight, setShowLight] = useState(true); const [themeState, themeSetter] = useContext(ThemeContext); const dispatchDarkTheme = () => themeSetter(DARK); const dispatchLightTheme = () => themeSetter(LIGHT); const toggleTheme = () => { showLight ? dispatchDarkTheme() : dispatchLightTheme(); setShowLight(!showLight); }; console.log(themeState); return ( {showLight ? "Đổi sang Giao diện tối" : "Đổi sang Giao diện sáng"} );};export default ThemeToggler;
useContext
để lấy các giá trị mà chúng tôi đã truyền cho ThemeContext.Provider
từ tệp App.js
của chúng tôi. Như đã hiển thị ở trên, các giá trị này bao gồm ThemeState
, hàm dispatch cho chủ đề sáng và hàm dispatch cho chủ đề tối. Sau đó, chúng tôi chỉ cần gọi các hàm dispatch để chuyển đổi các chủ đề. Chúng tôi cũng đã tạo một trạng thái showLight
để xác định chủ đề hiện tại. Điều này cho phép chúng tôi dễ dàng thay đổi văn bản nút tùy thuộc vào chủ đề hiện tại.Móc useMemo
Móc useMemo
được thiết kế để ghi nhớ các phép tính tốn kém. Ghi nhớ đơn giản có nghĩa là lưu trữ đệm. Nó lưu trữ đệm kết quả tính toán liên quan đến các giá trị phụ thuộc để khi các giá trị giống nhau được truyền, useMemo
sẽ chỉ đưa ra giá trị đã tính toán mà không tính toán lại lần nữa. Điều này có thể cải thiện đáng kể hiệu suất khi thực hiện đúng.Có thể sử dụng hook như sau:
Mã:
const memoizedResult = useMemo(() => expensiveComputation(a, b), [a, b])
useMemo
.- Khi các giá trị phụ thuộc a và b vẫn giữ nguyên.
HookuseMemo
sẽ trả về giá trị đã ghi nhớ được tính toán mà không cần tính toán lại. - Khi các giá trị phụ thuộc a và b thay đổi.
Hook sẽ tính toán lại giá trị. - Khi không có giá trị phụ thuộc nào được truyền vào.
Hook sẽ tính toán lại giá trị.
Trong ví dụ bên dưới, chúng ta sẽ tính toán PAYE và Thu nhập sau PAYE của nhân viên công ty với dữ liệu giả từ JSONPlaceholder.
Phép tính sẽ dựa trên quy trình tính thuế thu nhập cá nhân cho các nhà cung cấp tại Nigeria của PricewaterhouseCoopers có sẵn tại đây.
Điều này được hiển thị trong hộp cát bên dưới.
Xem thêm tại đây
Đầu tiên, chúng tôi truy vấn API để lấy dữ liệu của nhân viên. Chúng tôi cũng lấy dữ liệu cho từng nhân viên (theo id nhân viên của họ).
Mã:
const [employee, setEmployee] = useState({}); const [employees, setEmployees] = useState([]); const [num, setNum] = useState(1); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/employees"; useEffect(() => { const getEmployee = async () => { const { data } = await axios.get(`${endPoint}/${num}`); setEmployee(data); }; getEmployee(); }, [num]); useEffect(() => { axios.get(endPoint).then(({ data }) => setEmployees(data)); }, [num]);
axios
và phương thức async/await
trong useEffect
đầu tiên, sau đó là cú pháp dấu chấm then trong cú pháp thứ hai. Hai cách tiếp cận này hoạt động theo cùng một cách.Tiếp theo, sử dụng dữ liệu nhân viên mà chúng ta lấy được từ trên, hãy tính các biến cứu trợ:
Mã:
const taxVariablesCompute = useMemo(() => { const { income, noOfChildren, noOfDependentRelatives } = employee; //tính toán được cho là phức tạp //tính toán cứu trợ thuế cho relief Allowance, children relief, // relative relief và pension relief const reliefs = reliefAllowance1 + reliefAllowance2 + childrenRelief + relativeRelief + pensionRelief; return reliefs; }, [employee]);
useMemo
để ghi nhớ hoặc tối ưu hóa nó. Ghi nhớ theo cách này sẽ đảm bảo rằng phép tính sẽ không được tính lại nếu chúng ta cố gắng truy cập lại cùng một nhân viên.Hơn nữa, bằng cách sử dụng các giá trị giảm thuế thu được ở trên, chúng tôi muốn tính PAYE và thu nhập sau PAYE.
Mã:
const taxCalculation = useMemo(() => { const { income } = employee; let taxableIncome = income - taxVariablesCompute; let PAYE = 0; //tính toán phức tạp được cho là //tính toán để tính PAYE dựa trên thu nhập chịu thuế và điểm cuối thuế const netIncome = income - PAYE; return { PAYE, netIncome }; }, [employee, taxVariablesCompute]);
useMemo
hook.Toàn bộ mã có sẵn trên tại đây.
Điều này tuân theo quy trình tính thuế được đưa ra tại đây. Đầu tiên, chúng tôi tính toán khoản giảm thuế khi xem xét thu nhập, số con và số người thân phụ thuộc. Sau đó, chúng tôi nhân thu nhập chịu thuế với tỷ lệ PIT theo từng bước. Mặc dù phép tính đang đề cập không hoàn toàn cần thiết cho hướng dẫn này, nhưng nó được cung cấp để cho chúng ta thấy lý do tại sao
useMemo
có thể cần thiết. Đây cũng là một phép tính khá phức tạp nên chúng ta có thể cần ghi nhớ nó bằng useMemo
như được hiển thị ở trên.Sau khi tính toán các giá trị, chúng ta chỉ cần hiển thị kết quả.
Lưu ý những điều sau về hook
useMemo
.-
useMemo
chỉ nên được sử dụng khi cần tối ưu hóa phép tính. Nói cách khác, khi việc tính toán lại tốn kém. - Bạn nên viết phép tính trước mà không cần ghi nhớ và chỉ ghi nhớ nếu nó gây ra các vấn đề về hiệu suất.
- Việc sử dụng hook
useMemo
không cần thiết và không liên quan thậm chí có thể làm trầm trọng thêm các vấn đề về hiệu suất. - Đôi khi, ghi nhớ quá nhiều cũng có thể gây ra các vấn đề về hiệu suất.
Hook useCallback
useCallback
có cùng mục đích như useMemo
nhưng nó trả về một lệnh gọi lại được ghi nhớ thay vì một giá trị được ghi nhớ. Nói cách khác, useCallback
giống như việc truyền useMemo
mà không cần lệnh gọi hàm.Ví dụ, hãy xem xét các mã sau đây.
Mã:
import React, {useCallback, useMemo} from 'react'const MemoizationExample = () => { const a = 5 const b = 7 const memoResult = useMemo(() => a + b, [a, b]) const callbackResult = useCallback(a + b, [a, b]) console.log(memoResult) console.log(callbackResult) return( ... )}export default MemoizationExample
memoResult
và callbackResult
đều sẽ cho cùng một giá trị là 12
. Ở đây, useCallback
sẽ trả về một giá trị được ghi nhớ. Tuy nhiên, chúng ta cũng có thể khiến nó trả về một hàm gọi lại được ghi nhớ bằng cách truyền nó dưới dạng một hàm.useCallback
bên dưới sẽ trả về một hàm gọi lại được ghi nhớ.
Mã:
... const callbackResult = useCallback(() => a + b, [a, b])...
useEffect
.
Mã:
import {useCallback, useEffect} from 'react'const memoizationExample = () => { const a = 5 const b = 7 const callbackResult = useCallback(() => a + b, [a, b]) useEffect(() => { const callback = callbackResult() console.log(callback) }) return ( console.log(callbackResult())}> Trigger Callback )}export default memoizationExample
useCallback
. Sau đó, chúng tôi gọi hàm gọi lại trong một hook useEffect
khi thành phần được gắn kết và khi một nút được nhấp vào.Cả
useEffect
và nhấp vào nút đều mang lại cùng một kết quả.Lưu ý rằng các khái niệm, điều nên làm và không nên làm áp dụng cho hook
useMemo
cũng áp dụng cho hook useCallback
. Chúng ta có thể tạo lại ví dụ useMemo
bằng useCallback
.Hook useRef
useRef
trả về một đối tượng có thể tồn tại trong một ứng dụng. Hook chỉ có một thuộc tính, current
và chúng ta có thể dễ dàng truyền một đối số cho nó.Nó phục vụ cùng một mục đích như
createRef
được sử dụng trong các thành phần dựa trên lớp. Chúng ta có thể tạo tham chiếu bằng hook này như sau:
Mã:
const newRef = useRef('')
newRef
và truyền một chuỗi rỗng vào đó.Hook này chủ yếu được sử dụng cho hai mục đích:
- Truy cập hoặc thao tác DOM và
- Lưu trữ các trạng thái có thể thay đổi — điều này hữu ích khi chúng ta không muốn thành phần hiển thị lại khi giá trị thay đổi.
Thao tác DOM
Khi được truyền vào một phần tử DOM, đối tượng ref trỏ đến phần tử đó và có thể được sử dụng để truy cập các thuộc tính và thuộc tính DOM của phần tử đó.Sau đây là một ví dụ rất đơn giản để chứng minh khái niệm này.
Mã:
import React, {useRef, useEffect} from 'react'const RefExample = () = > { const headingRef = useRef('') console.log(headingRef) return( [HEADING=1]Đây là phần tử h1[/HEADING] )}export default RefExample
headingRef
bằng cách sử dụng hook useRef
truyền một chuỗi rỗng. Sau đó, chúng tôi đặt ref trong thẻ h1
bằng cách truyền ref = {headingRef}
. Bằng cách đặt ref này, chúng tôi đã yêu cầu headingRef
trỏ đến phần tử h1
của chúng tôi. Điều này có nghĩa là chúng ta có thể truy cập các thuộc tính của phần tử h1
từ ref.Để xem điều này, nếu chúng ta kiểm tra giá trị của
console.log(headingRef)
, chúng ta sẽ nhận được {current: HTMLHeadingElement}
hoặc {current: h1}
và chúng ta có thể đánh giá tất cả các thuộc tính hoặc thuộc tính của phần tử. Một điều tương tự áp dụng cho bất kỳ phần tử HTML nào khác.Ví dụ, chúng ta có thể làm cho văn bản in nghiêng khi thành phần được gắn kết.
Mã:
useEffect(() => { headingRef.current.style.fontStyle = "italic";}, []);
Mã:
... headingRef.current.innerHTML = "Một phần tử H1 đã thay đổi";...
Mã:
... headingRef.current.parentNode.style.backgroundColor = "red";...
headingRef.current
có thể được đọc theo cùng cách như document.querySelector('.topheading')
.Một trường hợp sử dụng thú vị của hook
useRef
trong việc thao tác phần tử DOM là tập trung con trỏ vào phần tử đầu vào. Chúng ta hãy nhanh chóng chạy qua nó.
Mã:
import {useRef, useEffect} from 'react'const inputRefExample = () => { const inputRef = useRef(null) useEffect(() => { inputRef.current.focus() }, []) return( inputRef.current.focus()}>Focus on Input )}export default inputRefExample
inputRef
bằng cách sử dụng hook useRef
rồi yêu cầu nó trỏ đến phần tử đầu vào. Sau đó, chúng tôi đã làm cho con trỏ tập trung vào tham chiếu đầu vào khi thành phần tải và khi nút được nhấp bằng cách sử dụng inputRef.current.focus()
. Điều này khả thi vì focus()
là một thuộc tính của các phần tử đầu vào và do đó ref sẽ có thể đánh giá các phương thức.Các ref được tạo trong một thành phần cha có thể được đánh giá tại thành phần con bằng cách chuyển tiếp nó bằng
React.forwardRef()
. Chúng ta hãy cùng xem xét nó.Trước tiên, hãy tạo một thành phần khác
NewInput.js
và thêm các mã sau vào đó.
Mã:
import { useRef, forwardRef } from "react";const NewInput = forwardRef((props, ref) => { return ;});export default NewInput;
props
và ref
. Chúng tôi đã truyền ref cho prop ref của nó và props.val
cho prop giữ chỗ của nó. Các thành phần React thông thường không sử dụng thuộc tính ref
. Thuộc tính này chỉ khả dụng khi chúng tôi bọc nó bằng React.forwardRef
như được hiển thị ở trên.Sau đó, chúng ta có thể dễ dàng gọi thuộc tính này trong thành phần cha.
Mã:
......
Lưu trữ các trạng thái có thể thay đổi
Các tham chiếu không chỉ được sử dụng để thao tác các phần tử DOM mà còn có thể được sử dụng để lưu trữ các giá trị có thể thay đổi mà không cần hiển thị lại toàn bộ thành phần.Ví dụ sau sẽ phát hiện số lần nhấp vào nút mà không cần hiển thị lại thành phần.
Mã:
import { useRef } from "react";export default function App() { const countRef = useRef(0); const increment = () => { countRef.current++; console.log(countRef); }; return ( Increment );}
countRef
khi nút được nhấp và sau đó ghi vào bảng điều khiển. Mặc dù giá trị được tăng như hiển thị trong bảng điều khiển, chúng ta sẽ không thể thấy bất kỳ thay đổi nào nếu chúng ta cố gắng đánh giá trực tiếp trong thành phần của mình. Nó sẽ chỉ cập nhật trong thành phần khi nó được hiển thị lại.Lưu ý rằng trong khi
useState
là không đồng bộ, useRef
là đồng bộ. Nói cách khác, giá trị có sẵn ngay sau khi được cập nhật.Móc useLayoutEffect
Giống như móc useEffect
, useLayoutEffect
được gọi sau khi thành phần được gắn kết và hiển thị. Móc này được kích hoạt sau khi đột biến DOM và thực hiện đồng bộ. Ngoài việc được gọi đồng bộ sau khi đột biến DOM, useLayoutEffect
thực hiện cùng một chức năng như useEffect
.useLayoutEffect
chỉ nên được sử dụng để thực hiện đột biến DOM hoặc phép đo liên quan đến DOM, nếu không, bạn nên sử dụng hook useEffect
. Sử dụng hook useEffect
cho các hàm đột biến DOM có thể gây ra một số vấn đề về hiệu suất như nhấp nháy nhưng useLayoutEffect
xử lý chúng một cách hoàn hảo vì nó chạy sau khi các đột biến đã xảy ra.Chúng ta hãy xem một số ví dụ để chứng minh khái niệm này.
- Chúng ta sẽ lấy chiều rộng và chiều cao của cửa sổ khi thay đổi kích thước.
Mã:
import {useState, useLayoutEffect} từ 'react'const ResizeExample = () =>{ const [windowSize, setWindowSize] = useState({width: 0, height: 0}) useLayoutEffect(() => { const resizeWindow = () => setWindowSize({ width: window.innerWidth, height: window.innerHeight }) window.addEventListener('resize', resizeWindow) return () => window.removeEventListener('resize', resizeWindow) }, []) return (
width: {windowSize.width}
height: {windowSize.height}
)}export default ResizeExample
windowSize
với các thuộc tính width và height. Sau đó, chúng ta đặt trạng thái thành chiều rộng và chiều cao của cửa sổ hiện tại tương ứng khi cửa sổ được thay đổi kích thước. Chúng ta cũng dọn dẹp mã khi nó được gỡ gắn kết. Quá trình dọn dẹp là điều cần thiết trong useLayoutEffect
để dọn dẹp thao tác DOM và cải thiện hiệu quả.- Hãy làm mờ một văn bản bằng
useLayoutEffect
.
Mã:
import { useRef, useState, useLayoutEffect } from "react";export default function App() { const paragraphRef = useRef(""); useLayoutEffect(() => { const { current } = paragraphRef; const bluredEffect = () => { current.style.color = "transparent"; current.style.textShadow = "0 0 5px rgba(0,0,0,0.5)"; }; current.addEventListener("click", blurEffect); return () => current.removeEventListener("click", blurEffect); }, []); return (
Đây là văn bản cần làm mờ
);}
useRef
và useLayoutEffect
cùng nhau trong đoạn mã trên. Đầu tiên, chúng tôi tạo một tham chiếu, paragraphRef
để trỏ đến đoạn văn của chúng tôi. Sau đó, chúng tôi tạo một trình lắng nghe sự kiện khi nhấp để theo dõi thời điểm đoạn văn được nhấp và sau đó làm mờ nó bằng các thuộc tính kiểu mà chúng tôi đã xác định. Cuối cùng, chúng tôi dọn dẹp trình lắng nghe sự kiện bằng cách sử dụng removeEventListener
.Các Hook useDispatch
và useSelector
useDispatch
là một hook Redux để phân phối (kích hoạt) các hành động trong một ứng dụng. Nó lấy một đối tượng hành động làm đối số và gọi hành động. useDispatch
là hook tương đương với mapDispatchToProps
của hook.Mặt khác,
useSelector
là một hook Redux để đánh giá các trạng thái Redux. Nó sử dụng một hàm để chọn trình giảm Redux chính xác từ kho lưu trữ và sau đó trả về các trạng thái tương ứng.Sau khi kho lưu trữ Redux của chúng ta được kết nối với ứng dụng React thông qua nhà cung cấp Redux, chúng ta có thể gọi các hành động bằng
useDispatch
và truy cập các trạng thái bằng useSelector
. Mọi hành động và trạng thái Redux đều có thể được đánh giá bằng hai hook này.Lưu ý rằng các trạng thái này đi kèm với React Redux (một gói giúp đánh giá kho lưu trữ Redux dễ dàng trong ứng dụng React). Chúng không có sẵn trong thư viện Redux cốt lõi.
Các hook này rất dễ sử dụng. Trước tiên, chúng ta phải khai báo hàm dispatch và sau đó kích hoạt nó.
Mã:
import {useDispatch, useSelector} from 'react-redux'import {useEffect} from 'react'const myaction from '...'const ReduxHooksExample = () =>{ const dispatch = useDispatch() useEffect(() => { dispatch(myaction()); //hoặc, chúng ta có thể thực hiện lệnh dispatch({type: 'MY_ACTION_TYPE'}) }, []) const mystate = useSelector(state => state.myReducerstate) return( ... )}export default ReduxHooksExample
useDispatch
và useSelector
from react-redux
. Sau đó, trong một hook useEffect
, chúng ta đã phân phối hành động. Chúng ta có thể định nghĩa hành động trong một tệp khác rồi gọi nó ở đây hoặc chúng ta có thể định nghĩa trực tiếp như được hiển thị trong lệnh gọi useEffect
.Sau khi phân phối các hành động, các trạng thái của chúng ta sẽ khả dụng. Sau đó, chúng ta có thể truy xuất trạng thái bằng hook
useSelector
như được hiển thị. Các trạng thái có thể được sử dụng theo cùng cách chúng ta sử dụng các trạng thái từ hook useState
.Chúng ta hãy xem một ví dụ để chứng minh hai hook này.
Để chứng minh khái niệm này, chúng ta phải tạo một kho lưu trữ Redux, bộ giảm và các hành động. Để đơn giản hóa mọi thứ ở đây, chúng ta sẽ sử dụng thư viện Redux Toolkit với cơ sở dữ liệu giả của mình từ JSONPlaceholder.
Chúng ta cần cài đặt các gói sau để bắt đầu. Chạy các lệnh bash sau.
Mã:
npm i redux @reduxjs/toolkit react-redux axios
employeesSlice.js
để xử lý bộ giảm tốc và hành động cho API của nhân viên.
Mã:
import { createAsyncThunk, createSlice } từ "@reduxjs/toolkit";import axios từ "axios";const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/employees";export const fetchEmployees = createAsyncThunk("employees/fetchAll", async () => { const { data } = await axios.get(endPoint); return data;});const employeesSlice = createSlice({ name: "employees", initialState: { employees: [], loading: false, error: "" }, reducers: {}, extraReducers: { [fetchEmployees.pending]: (state, action) => { state.status = "loading"; }, [fetchEmployees.fulfilled]: (state, action) => { state.status = "success"; state.employees = action.payload; }, [fetchEmployees.rejected]: (state, action) => { state.status = "error"; state.error = action.error.message; } }});export default employeesSlice.reducer;
createAsyncThunk
để truy cập phần mềm trung gian Thunk
để thực hiện các hành động bất đồng bộ. Điều này cho phép chúng tôi lấy danh sách nhân viên từ API. Sau đó, chúng tôi đã tạo employeesSlice
và trả về, "loading", "error" và dữ liệu của nhân viên tùy thuộc vào loại hành động.Bộ công cụ Redux cũng giúp thiết lập cửa hàng dễ dàng. Đây là cửa hàng.
Mã:
import { configureStore } from "@reduxjs/toolkit";import { combineReducers } from "redux";import employeesReducer from "./employeesSlice";const reducer = combineReducers({ employees: employeesReducer});export default configureStore({ reducer });;
combineReducers
để đóng gói các reducer và hàm configureStore
do bộ công cụ Redux cung cấp để thiết lập store.Chúng ta hãy tiến hành sử dụng hàm này trong ứng dụng của mình.
Trước tiên, chúng ta cần kết nối Redux với ứng dụng React của mình. Lý tưởng nhất là nên thực hiện việc này ở gốc ứng dụng. Tôi muốn thực hiện trong tệp
index.js
.
Mã:
import React, { StrictMode } from "react";import ReactDOM from "react-dom";import store from "./redux/store";import { Provider } from "react-redux";import App from "./App";const rootElement = document.getElementById("root");ReactDOM.render(
, rootElement);
Provider
từ react-redux
.Sau đó, tôi đã bọc toàn bộ ứng dụng bằng hàm
Provider
, truyền store cho nó. Điều này làm cho cửa hàng có thể truy cập được trong toàn bộ ứng dụng của chúng ta.Sau đó, chúng ta có thể tiến hành sử dụng các hook
useDispatch
và useSelector
để lấy dữ liệu.Hãy thực hiện điều này trong tệp
App.js
của chúng ta.
Mã:
import { useDispatch, useSelector } from "react-redux";import { fetchEmployees } from "./redux/employeesSlice";import { useEffect } from "react";export default function App() { const dispatch = useDispatch(); useEffect(() => { dispatch(fetchEmployees()); }, [dispatch]); const employeesState = useSelector((state) => state.employees); const { employees, loading, error } = employeesState; trả về ( {đang tải ? ( "Đang tải..." ) : lỗi ? ( {lỗi} ) : ( [HEADING=1]Danh sách nhân viên[/HEADING] {employees.map((employee) = > ( [HEADING=3]{`${employee.firstName} ${employee.lastName}`}[/HEADING] ))} )} );}
useDispatch
để gọi hành động fetchEmployees
được tạo trong tệp employeesSlice.js
. Điều này làm cho trạng thái của nhân viên có sẵn trong ứng dụng của chúng tôi. Sau đó, chúng tôi đã sử dụng hook useSelector
để lấy các trạng thái. Sau đó, chúng tôi hiển thị kết quả bằng cách ánh xạ qua employees
.Hook useHistory
Điều hướng rất quan trọng trong ứng dụng React. Mặc dù bạn có thể thực hiện điều này theo một số cách, nhưng React Router cung cấp một cách đơn giản, hiệu quả và phổ biến để đạt được định tuyến động trong ứng dụng React. Hơn nữa, React Router cung cấp một vài hook để đánh giá trạng thái của router và thực hiện điều hướng trên trình duyệt nhưng để sử dụng chúng, trước tiên bạn cần thiết lập ứng dụng của mình đúng cách.Để sử dụng bất kỳ hook React Router nào, trước tiên chúng ta nên bao bọc ứng dụng của mình bằng
BrowserRouter
. Sau đó, chúng ta có thể lồng các tuyến đường bằng Switch
và Route
.Nhưng trước tiên, chúng ta phải cài đặt gói bằng cách chạy các lệnh sau.
Mã:
npm install react-router-dom
App.js
của mình.
Mã:
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";import Employees from "./components/Employees";export default function App() { return ( ... );}
Employees
. Thuộc tính path
cho React Router DOM biết đường dẫn của thành phần và có thể được đánh giá bằng chuỗi truy vấn hoặc nhiều phương pháp khác.Thứ tự rất quan trọng ở đây. Tuyến gốc phải được đặt bên dưới tuyến con, v.v. Để ghi đè thứ tự này, bạn cần đưa từ khóa
exact
vào tuyến gốc.
Mã:
useHistory
và các hook React Router khác trong ứng dụng của mình.Để sử dụng hook
useHistory
, trước tiên chúng ta cần khai báo nó như sau.
Mã:
import {useHistory} from 'history'import {useHistory} from 'react-router-dom'const Employees = () =>{ const history = useHistory() ...}
block
, createHref
, go
, goBack
, goForward
, length
, listen
, location
, push
, replace
. Mặc dù tất cả các thuộc tính này đều hữu ích, nhưng bạn có thể sẽ sử dụng history.push
và history.replace
thường xuyên hơn các thuộc tính khác.Hãy sử dụng thuộc tính này để di chuyển từ trang này sang trang khác.
Giả sử chúng ta muốn tìm dữ liệu về một nhân viên cụ thể khi nhấp vào tên của họ. Chúng ta có thể sử dụng hook
useHistory
để điều hướng đến trang mới nơi thông tin của nhân viên sẽ được hiển thị.
Mã:
function moveToPage = (id) =>{ history.push(`/employees/${id}`)}
Employee.js
của mình bằng cách thêm nội dung sau.
Mã:
import { useEffect } from "react";import { Link, useHistory, useLocation } from "react-router-dom";export default function Employees() { const history = useHistory(); function pushToPage = (id) => { history.push(`/employees/${id}`) } ... return ( ... [HEADING=1]Danh sách nhân viên[/HEADING] {employees.map((employee) => ( {`${employee.firstName} ${employee.lastName} `} » ))} );}
pushToPage
, chúng tôi đã sử dụng history
từ hook useHistory
để điều hướng đến trang của nhân viên và truyền id nhân viên bên cạnh.Hook useLocation
Hook này cũng đi kèm với React Router DOM. Đây là một hook rất phổ biến được sử dụng để làm việc với tham số chuỗi truy vấn. Hook này tương tự như window.location
trong trình duyệt.
Mã:
import {useLocation} from 'react'const LocationExample = () =>{ const location = useLocation() return ( ... )}export default LocationExample
useLocation
trả về pathname
, tham số search
, hash
và state
. Các tham số được sử dụng phổ biến nhất bao gồm pathname
và search
nhưng bạn cũng có thể sử dụng hash
và state
rất nhiều trong ứng dụng của mình.Thuộc tính
pathname
của location sẽ trả về đường dẫn mà chúng ta đã đặt trong thiết lập Route
của mình. Trong khi search
sẽ trả về tham số tìm kiếm truy vấn nếu có. Ví dụ, nếu chúng ta truyền 'http://mywebsite.com/employee/?id=1'
vào truy vấn của mình, pathname
sẽ là /employee
và search
sẽ là ?id=1
.Sau đó, chúng ta có thể truy xuất các tham số tìm kiếm khác nhau bằng các gói như query-string hoặc bằng cách mã hóa chúng.
Móc useParams
Nếu chúng ta thiết lập Tuyến đường của mình với tham số URL trong thuộc tính đường dẫn của nó, chúng ta có thể đánh giá các tham số đó dưới dạng cặp khóa/giá trị bằng móc useParams
.Ví dụ, giả sử rằng chúng ta có Tuyến đường sau.
Mã:
:id
.Với hook
useParams
, chúng ta có thể đánh giá id do người dùng truyền, nếu có.Ví dụ, giả sử người dùng truyền nội dung sau trong hàm với
history.push
,
Mã:
function goToPage = () => { history.push(`/employee/3`)}
useParams
để truy cập tham số URL này như sau.
Mã:
import {useParams} from 'react-router-dom'const ParamsExample = () =>{ const params = useParams() console.log(params) return( ... )}export default ParamsExample
params
vào bảng điều khiển, chúng ta sẽ nhận được đối tượng sau {id: "3"}
.Hook useRouteMatch
Hook này cung cấp quyền truy cập vào đối tượng khớp. Nó trả về kết quả khớp gần nhất với một thành phần nếu không có đối số nào được cung cấp cho nó.Đối tượng khớp trả về một số tham số bao gồm
path
(giống như đường dẫn được chỉ định trong Route), URL
, đối tượng params
và isExact
.Ví dụ, chúng ta có thể sử dụng
useRouteMatch
để trả về các thành phần dựa trên tuyến đường.
Mã:
import { useRouteMatch } from "react-router-dom";import Employees from "...";import Admin from "..."const CustomRoute = () => { const match = useRouteMatch("/employees/:id"); return match ? ( ) : ( );};export default CustomRoute;
useRouteMatch
rồi render thành phần
hoặc
tùy thuộc vào tuyến đường mà người dùng chọn.Để điều này hoạt động, chúng ta vẫn cần thêm tuyến đường vào tệp
App.js
của mình.
Mã:
... ...
Xây dựng một Hook tùy chỉnh
Theo tài liệu React, xây dựng một hook tùy chỉnh cho phép chúng ta trích xuất logic thành một hàm có thể sử dụng lại. Tuy nhiên, bạn cần đảm bảo rằng tất cả các quy tắc áp dụng cho React hook đều áp dụng cho custom hook của bạn. Kiểm tra các quy tắc của React hook ở đầu hướng dẫn này và đảm bảo rằng custom hook của bạn tuân thủ từng quy tắc đó.Custom hook cho phép chúng ta viết hàm một lần và sử dụng lại bất cứ khi nào cần và do đó tuân thủ nguyên tắc DRY.
Ví dụ, chúng ta có thể tạo một custom hook để lấy vị trí cuộn trên trang của mình như sau.
Mã:
import { useLayoutEffect, useState } from "react";export const useScrollPos = () => { const [scrollPos, setScrollPos] = useState({ x: 0, y: 0 }); useLayoutEffect(() => { const getScrollPos = () => setScrollPos({ x: window.pageXOffset, y: window.pageYOffset }); window.addEventListener("scroll", getScrollPos); return () => window.removeEventListener("scroll", getScrollPos); }, []); return scrollPos;};
scrollPos
, để lưu trữ vị trí cuộn. Vì điều này sẽ sửa đổi DOM, chúng tôi cần sử dụng useLayoutEffect
thay vì useEffect
. Chúng tôi đã thêm một trình lắng nghe sự kiện cuộn để nắm bắt các vị trí cuộn x và y, sau đó dọn dẹp trình lắng nghe sự kiện. Cuối cùng, chúng ta quay lại vị trí cuộn.Chúng ta có thể sử dụng hook tùy chỉnh này ở bất kỳ đâu trong ứng dụng của mình bằng cách gọi nó và sử dụng nó giống như chúng ta sử dụng bất kỳ trạng thái nào khác.
Mã:
import {useScrollPos} from './Scroll'const App = () =>{ const scrollPos = useScrollPos() console.log(scrollPos.x, scrollPos.y) return ( ... )}export default App
useScrollPos
mà chúng ta đã tạo ở trên. Sau đó, chúng ta đã khởi tạo nó và sau đó ghi lại giá trị vào bảng điều khiển của mình. Nếu chúng ta cuộn trên trang, hook sẽ hiển thị cho chúng ta vị trí cuộn ở mọi bước cuộn.Chúng ta có thể tạo các hook tùy chỉnh để thực hiện bất kỳ điều gì chúng ta có thể tưởng tượng trong ứng dụng của mình. Như bạn thấy, chúng ta chỉ cần sử dụng hook React tích hợp để thực hiện một số chức năng. Chúng ta cũng có thể sử dụng các thư viện của bên thứ ba để tạo các hook tùy chỉnh nhưng nếu làm như vậy, chúng ta sẽ phải cài đặt thư viện đó để có thể sử dụng hook.
Kết luận
Trong hướng dẫn này, chúng tôi đã xem xét kỹ một số hook React hữu ích mà bạn sẽ sử dụng trong hầu hết các ứng dụng của mình. Chúng tôi đã xem xét những gì chúng trình bày và cách sử dụng chúng trong ứng dụng của bạn. Chúng tôi cũng đã xem xét một số ví dụ mã để giúp bạn hiểu các hook này và áp dụng chúng vào ứng dụng của mình.Tôi khuyến khích bạn thử các hook này trong ứng dụng của riêng bạn để hiểu rõ hơn về chúng.
Tài nguyên từ React Docs
- Câu hỏi thường gặp về Hook
- Redux Toolkit
- Sử dụng State Hook
- Sử dụng Effect Hook
- Tài liệu tham khảo API Hook
- React Redux Hooks
- React Router Hooks
Đọc thêm
- Hướng dẫn sử dụng Redux Toolkit với TypeScript
- Tại sao tối ưu hóa điểm Lighthouse không đủ để có một trang web nhanh
- Giải quyết các vấn đề về đối tượng phương tiện nổi bằng ngữ cảnh định dạng khối CSS
- Tạo biểu mẫu nhiều bước hiệu quả để có trải nghiệm người dùng tốt hơn