React Hooks hữu ích mà bạn có thể sử dụng trong dự án của mình

theanh

Administrator
Nhân viên
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.

Độ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 caocá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)
Trong đó mapStateToPropsmapDispatchToProps 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 useSelectoruseDispatch.

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  );}
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.

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.
  1. 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.
  2. 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.
  3. 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'
Để 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.
Mã:
const [state, updaterFn] = useState('')
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.
Mã:
const [count, setCount] = useState(0)
Lưu ý rằng giá trị ban đầu của trạng thái 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 hook useState 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
Trong đoạn mã trên, trước tiên chúng ta nhập hook 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
Trong đoạn mã trên, chúng ta đã tạo hai trạng thái arrobj, 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à handleArrClickhandleObjClick để đặ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 nameage.

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, componentWillUnmountcomponentDidUpdate 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(  ...  )}
Trong mã trên, chúng tôi chỉ cần ghi 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] ...  )}
Hook 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.
  1. Khi gắn kết — sau khi thành phần được hiển thị.
  2. Khi giá trị của count thay đổi.
Khi gắn kết, biểu thức 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')})
Hook 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ý})
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ý 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}
  ))}  );}
Trong đoạn mã trên, chúng ta đã tạo trạng thái 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  );}
Ở đây chúng tôi đã tạo hai hook 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]);
Tại đây, chúng ta đã truyền mã thông báo của Axios làm tham số thứ hai cho 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)
Ở đây, 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;
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 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)}>✓  ))}   );}
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 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 }]
và sau đó truyền nó vào hook useReducer của chúng ta.
Mã:
const [state, dispatch] = useReducer(reducer, initialState, initFunc)
Nếu chúng ta làm điều này, 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;
Ở đây, chúng ta đã tạo một ngữ cảnh và truyền giá trị ban đầu là 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;
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 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 . 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;
Tại đây, chúng tôi đã truy cập ngữ cảnh bằng cách sử dụng hook 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 useContextuseReducer

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);
Ở đâ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 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)} />  +      );}
Tại đây, chúng tôi đã gói tệp 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;
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 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;
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 completeTodoremoveTodo 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);
Tại đây, chúng ta đã tạo một ngữ cảnh và truyền 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;
Trong mã trên, chúng ta đã tạo một đối tượng colors chứa các thuộc tính sáng và tối. Mỗi thuộc tính có đối tượng backgroundColorcolor.

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;
Giống như tất cả các bộ giảm tốc, 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)} />  +       );}
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 ThemeContext, themeReducer, ThemeTogglerColors. 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ề themeStatethemeDispatch 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 themeStatedispatch 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;
Trong thành phần này, chúng tôi đã sử dụng hook 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])
Hãy xem xét ba trường hợp của hook useMemo.
  1. Khi các giá trị phụ thuộc a và b vẫn giữ nguyên.
    Hook useMemo sẽ trả về giá trị đã ghi nhớ được tính toán mà không cần tính toán lại.
  2. Khi các giá trị phụ thuộc a và b thay đổi.
    Hook sẽ tính toán lại giá trị.
  3. 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ị.
Hãy xem một ví dụ để chứng minh khái niệm này.

Trong ví dụ bên dưới, chúng ta sẽ tính toán PAYEThu 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]);
Chúng tôi đã sử dụng 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]);
Đâ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 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]);
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 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
Trong ví dụ trên, cả memoResultcallbackResult đề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])...
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 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
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 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('')
Ở đây chúng ta tạo một ref mới có tên là 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:
  1. Truy cập hoặc thao tác DOM và
  2. 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
Trong ví dụ trên, chúng tôi đã định nghĩa 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";}, []);
Chúng ta thậm chí có thể thay đổi văn bản thành thứ khác.
Mã:
... headingRef.current.innerHTML = "Một phần tử H1 đã thay đổi";...
Chúng ta thậm chí có thể thay đổi màu nền của vùng chứa cha.
Mã:
... headingRef.current.parentNode.style.backgroundColor = "red";...
Có thể thực hiện bất kỳ thao tác DOM nào ở đây. Lưu ý rằng 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
Trong đoạn mã trên, chúng tôi đã tạo 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;
Thành phần này chấp nhận propsref. 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   );}
Trong đoạn mã trên, chúng ta đã tăng 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.
  1. 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
Trong đoạn mã trên, chúng ta đã tạo một trạng thái 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ả.
  1. 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ờ
  );}
Chúng tôi đã sử dụng useRefuseLayoutEffect 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 useDispatchuseSelector

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
Trong đoạn mã trên, chúng ta đã import useDispatchuseSelector 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
Đầu tiên, hãy tạo 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;
Đây là thiết lập chuẩn cho bộ công cụ Redux. Chúng tôi đã sử dụng 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 });;
Ở đây, chúng tôi đã sử dụng 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);
Ở đây, tôi đã nhập store mà tôi đã tạo ở trên và cả 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 useDispatchuseSelector để 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]  ))}  )}  );}
Trong đoạn mã trên, chúng tôi đã sử dụng hook 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 SwitchRoute.

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
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 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 (       ...    );}
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 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ã:
Bây giờ chúng ta đã thiết lập bộ định tuyến, sau đó chúng ta có thể sử dụng hook 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() ...}
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 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.pushhistory.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}`)}
Chúng ta có thể triển khai điều này trong tệp 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} `}  »   ))}  );}
Trong hàm 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
Hook useLocation trả về pathname, tham số search, hashstate. Các tham số được sử dụng phổ biến nhất bao gồm pathnamesearch nhưng bạn cũng có thể sử dụng hashstate 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à /employeesearch 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ã:
Tuyến đường sẽ mong đợi một id động thay cho :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`)}
Chúng ta có thể sử dụng hook 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
Nếu chúng ta ghi 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 paramsisExact.

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;
Trong đoạn mã trên, chúng ta đặt đường dẫn của tuyến đường với 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;};
Ở đâ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, 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
Tại đây, chúng ta đã nhập hook tùy chỉnh 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 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
 
Back
Bên trên