React Hook là gì? Tại sao React Hook quan trọng trong dự án?
React Hook là một tính năng quan trọng của React, cho phép các functional component sử dụng state và lifecycle methods một cách dễ dàng và linh hoạt. React Hook đã thay đổi cách chúng ta viết React components, giúp chúng ta giảm thiểu sự phụ thuộc vào class component và thay vào đó tập trung vào việc phát triển các functional component.
Trong bài viết này, chúng ta sẽ tìm hiểu các component quan trọng nhất của React Hook và cách chúng được sử dụng.
I. React Hook là gì?
React Hook là một tính năng của React giúp quản lý state và xử lý side effect trong functional component. Nó giúp giảm bớt boilerplate code, làm cho code đơn giản và dễ đọc hơn. Tuy nhiên, việc sử dụng React Hook cũng đòi hỏi kiến thức về React và các quy tắc best practices của Hook để tránh gặp phải các vấn đề liên quan đến performance hay logic của ứng dụng.
II. Các Components trong React Hook
1. useState()
useState()
là một trong những Hook được cung cấp bởi thư viện React Hook. Hook là một tính năng mới được giới thiệu trong React 16.8, cho phép bạn sử dụng state và các tính năng của React trong hàm function, thay vì phải sử dụng class component như trước đây.
Bài viết này được đăng tại [free tuts .net]
useState()
cho phép bạn khởi tạo và sử dụng state trong một component. Khi sử dụng useState()
, bạn sẽ truyền vào giá trị khởi tạo của state và hook sẽ trả về một mảng với hai phần tử, phần tử đầu tiên là giá trị hiện tại của state và phần tử thứ hai là một hàm để cập nhật giá trị của state.
Ví Dụ:
import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); function increment() { setCount(count + 1); } return ( <div> <p>You clicked {count} times</p> <button onClick={increment}>Click me</button> </div> ); }
Trong ví dụ trên, chúng ta sử dụng useState()
để khởi tạo một biến state count với giá trị ban đầu là 0. Sau đó, chúng ta sử dụng hàm setCount để cập nhật giá trị của biến count khi người dùng click vào nút. Cuối cùng, chúng ta trả về các giá trị hiện tại của biến count và một nút để tăng giá trị của biến count lên mỗi khi người dùng click vào nó.
2. useEffect()
useEffect()
là một trong những API quan trọng của React Hook, nó giúp chúng ta xử lý side effect trong functional component.
useEffect()
được sử dụng để thực thi các side effect như tạo, cập nhật hoặc xóa các thành phần UI, gửi các yêu cầu mạng, hoặc thực hiện các hành động không đồng bộ khác. useEffect()
nhận vào một hàm callback và một mảng dependency. Hàm callback được gọi sau khi React thực hiện việc render component lần đầu tiên và sau mỗi lần component được cập nhật. Nếu mảng dependency không được cung cấp, useEffect()
sẽ được gọi sau mỗi lần component được cập nhật.
Khi các giá trị trong mảng dependency thay đổi, useEffect()
sẽ được gọi lại. Việc này giúp chúng ta tối ưu hóa các side effect và tránh gọi lại các side effect không cần thiết.
useEffect()
cũng có thể được sử dụng để hủy các side effect bằng cách trả về một hàm cleanup từ hàm callback.
Ví dụ, hãy xem xét trường hợp sử dụng useEffect()
để gọi một API mỗi khi component được hiển thị:
import { useState, useEffect } from 'react'; function MyComponent() { const [data, setData] = useState(null); useEffect(() => { fetch('https://my-api.com/data') .then(response => response.json()) .then(data => setData(data)) .catch(error => console.error(error)); }, []); return ( <div> {data ? ( <ul> {data.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> ) : ( <p>Loading data...</p> )} </div> ); }
Ở đây, chúng ta sử dụng useEffect()
để gọi API và cập nhật state khi dữ liệu được tải xuống. Nếu không có mảng dependency, useEffect()
sẽ được gọi sau mỗi lần component được cập nhật, dẫn đến việc gọi API không cần thiết. Bằng cách cung cấp một mảng dependency trống, useEffect()
chỉ được gọi khi component được hiển thị lần đầu tiên.
3. useContext()
useContext()
là một API của React Hook cho phép chúng ta truy cập và sử dụng giá trị được chia sẻ từ một Provider component đến các component con của nó.
useContext()
nhận vào một đối tượng context, được tạo bởi hàm createContext()
, và trả về giá trị hiện tại của context. Nếu giá trị context thay đổi, tất cả các component sử dụng useContext()
để truy cập context đó sẽ được cập nhật lại.
Ví dụ, hãy xem xét một ứng dụng Todo với một context để lưu trữ danh sách các công việc. Đầu tiên, chúng ta tạo một context với createContext()
:
import { createContext } from 'react'; export const TodoContext = createContext([]);
Sau đó, chúng ta tạo một Provider component, cung cấp giá trị context cho tất cả các component con của nó:
import { useState } from 'react'; import { TodoContext } from './TodoContext'; function TodoProvider({ children }) { const [todos, setTodos] = useState([]); const addTodo = (newTodo) => { setTodos([...todos, newTodo]); }; return ( <TodoContext.Provider value={{ todos, addTodo }}> {children} </TodoContext.Provider> ); } export default TodoProvider;
Ở đây, chúng ta sử dụng useState()
để tạo state todos và hàm setTodos để cập nhật state đó. Chúng ta cũng tạo một hàm addTodo để thêm một công việc mới vào danh sách. Cuối cùng, chúng ta truyền giá trị todos và addTodo vào Provider component.
Sau đó, chúng ta có thể sử dụng useContext()
để truy cập giá trị context này từ bất kỳ component con nào của Provider component:
import { useContext } from 'react'; import { TodoContext } from './TodoContext'; function TodoList() { const { todos } = useContext(TodoContext); return ( <ul> {todos.map((todo) => ( <li key={todo.id}>{todo.text}</li> ))} </ul> ); } export default TodoList;
Ở đây, chúng ta sử dụng useContext()
để truy cập danh sách todos từ context. Sau đó, chúng ta có thể sử dụng giá trị todos này để hiển thị danh sách công việc.
4. useReducer()
useReducer()
là một API của React Hook cho phép chúng ta quản lý state của một component bằng cách sử dụng reducer pattern giống như trong Redux.
Khi sử dụng useReducer()
, chúng ta cần truyền vào một reducer function và một initial state cho state của component. Reducer function nhận vào state hiện tại và một action và trả về state mới.
Ví dụ, hãy xem xét một ứng dụng Todo, chúng ta có thể sử dụng useReducer()
để quản lý state của danh sách các công việc:
import { useReducer } from 'react'; const initialTodos = []; function reducer(state, action) { switch (action.type) { case 'ADD_TODO': return [...state, action.payload]; case 'TOGGLE_TODO': return state.map(todo => todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo ); case 'DELETE_TODO': return state.filter(todo => todo.id !== action.payload); default: return state; } } function TodoList() { const [todos, dispatch] = useReducer(reducer, initialTodos); const addTodo = (newTodo) => { dispatch({ type: 'ADD_TODO', payload: newTodo }); }; const toggleTodo = (id) => { dispatch({ type: 'TOGGLE_TODO', payload: id }); }; const deleteTodo = (id) => { dispatch({ type: 'DELETE_TODO', payload: id }); }; return ( <ul> {todos.map((todo) => ( <li key={todo.id}> <span onClick={() => toggleTodo(todo.id)} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> <button onClick={() => deleteTodo(todo.id)}>Delete</button> </li> ))} </ul> ); } export default TodoList;
Ở đây, chúng ta khai báo reducer function và initialTodos. Sau đó, chúng ta sử dụng useReducer()
để khởi tạo state todos và hàm dispatch để gửi các action đến reducer function. Chúng ta cũng khai báo các hàm addTodo, toggleTodo và deleteTodo để thêm, đánh dấu hoàn thành hoặc xóa một công việc.
Sau đó, chúng ta sử dụng todos và dispatch để hiển thị danh sách công việc và xử lý các sự kiện như đánh dấu hoàn thành hoặc xóa một công việc. Khi dispatch một action, reducer function sẽ được gọi và trả về state mới, dẫn đến cập nhật lại UI của component.
5. useCallback()
Trong React Hook, useCallback()
là một trong những hook quan trọng được sử dụng để tối ưu hóa hiệu suất của các functional component. useCallback()
được sử dụng để memoize một hàm, nghĩa là lưu trữ phiên bản được lưu trữ trong bộ nhớ đến khi các đối số được truyền vào thay đổi. Việc memoize một hàm giúp tăng hiệu suất của component bằng cách tránh việc render lại khi không cần thiết.
Ví dụ, nếu bạn có một functional component và một hàm được truyền vào đó dưới dạng props, nhưng hàm đó không thay đổi giá trị của props được truyền vào, thì mỗi lần component được render lại, hàm đó cũng sẽ được tạo lại. Việc tạo lại hàm này có thể làm giảm hiệu suất của component.
Để giải quyết vấn đề này, bạn có thể sử dụng useCallback()
để memoize hàm đó. useCallback()
sẽ trả về một phiên bản của hàm được memoize và lưu trữ nó trong bộ nhớ đến khi các đối số được truyền vào thay đổi. Khi component được render lại, useCallback()
sẽ trả về phiên bản được lưu trữ trong bộ nhớ thay vì tạo ra một phiên bản mới của hàm.
Ví Dụ:
import React, { useCallback } from 'react'; function MyComponent({ onClick }) { const handleClick = useCallback(() => { onClick('clicked'); }, [onClick]); return <button onClick={handleClick}>Click me</button>; }
Trong ví dụ này, handleClick()
là một hàm được tạo ra bởi useCallback()
và được truyền vào button dưới dạng props onClick. useCallback()
sẽ lưu trữ phiên bản của handleClick()
trong bộ nhớ và trả về nó cho mỗi lần component được render lại, tránh việc tạo lại hàm này mỗi lần component được render lại.
6. useMemo()
Trong React Hook, useMemo()
là một trong những hook quan trọng được sử dụng để tối ưu hóa hiệu suất của các functional component. useMemo()
được sử dụng để memoize một giá trị, nghĩa là lưu trữ phiên bản của giá trị trong bộ nhớ đến khi các đối số được truyền vào thay đổi. Việc memoize một giá trị giúp tăng hiệu suất của component bằng cách tránh việc tính toán lại giá trị khi không cần thiết.
Ví dụ, nếu bạn có một functional component và một hàm được truyền vào đó dưới dạng props, và hàm đó trả về một giá trị, nhưng giá trị đó không thay đổi khi các đối số được truyền vào thay đổi, thì mỗi lần component được render lại, hàm đó cũng sẽ được tính toán lại. Việc tính toán lại giá trị này có thể làm giảm hiệu suất của component.
Để giải quyết vấn đề này, bạn có thể sử dụng useMemo()
để memoize giá trị đó. useMemo()
sẽ trả về một phiên bản của giá trị được memoize và lưu trữ nó trong bộ nhớ đến khi các đối số được truyền vào thay đổi. Khi component được render lại, useMemo()
sẽ trả về phiên bản được lưu trữ trong bộ nhớ thay vì tính toán lại giá trị.
Ví Dụ:
import React, { useMemo } from 'react'; function MyComponent({ a, b }) { const result = useMemo(() => { return a + b; }, [a, b]); return <div>{result}</div>; }
Trong ví dụ này, result
là một giá trị được tạo ra bởi useMemo()
và được trả về trong component. useMemo()
sẽ lưu trữ phiên bản của result
trong bộ nhớ và trả về nó cho mỗi lần component được render lại, tránh việc tính toán lại giá trị này mỗi lần component được render lại. Các đối số a và b được truyền vào useMemo()
để nó biết khi nào cần tính toán lại giá trị.
7. useRef()
Trong React Hook, useRef()
là một trong những hook quan trọng được sử dụng để tham chiếu đến một phần tử DOM hoặc lưu trữ một biến không thay đổi giữa các lần render của component.
useRef()
trả về một đối tượng được gọi là ref object, được bao bọc bởi một current property, giá trị của current property sẽ được lưu trữ và truy cập giữa các lần render của component. Khi giá trị của current property được cập nhật bên trong component, React không thể nhận ra sự thay đổi đó và không render lại component.
Bạn có thể sử dụng useRef()
để lưu trữ một tham chiếu đến một phần tử DOM và thay đổi thuộc tính của phần tử đó trong component.
Ví Dụ:
import React, { useRef, useEffect } from 'react'; function MyComponent() { const myRef = useRef(null); useEffect(() => { myRef.current.textContent = 'Hello, world!'; }, []); return <div ref={myRef}></div>; }
Trong ví dụ này, chúng ta sử dụng useRef()
để lưu trữ một tham chiếu đến một phần tử DOM. Sau đó, chúng ta sử dụng useEffect()
để thay đổi thuộc tính textContent()
của phần tử đó thành "Hello, world!" khi component được render lần đầu tiên. Cuối cùng, chúng ta truyền tham chiếu đến phần tử đó cho thuộc tính ref của div bằng cách sử dụng myRef.current.
Bạn cũng có thể sử dụng useRef()
để lưu trữ một biến không thay đổi giữa các lần render của component.
Ví Dụ:
import React, { useRef } from 'react'; function MyComponent() { const countRef = useRef(0); const incrementCount = () => { countRef.current += 1; console.log(`Count is ${countRef.current}`); }; return ( <div> <button onClick={incrementCount}>Increment count</button> </div> ); }
Trong ví dụ này, chúng ta sử dụng useRef()
để lưu trữ một biến countRef()
và khởi tạo giá trị ban đầu của nó là 0. Sau đó, chúng ta sử dụng một hàm incrementCount
để tăng giá trị của countRef()
lên 1 và in ra giá trị mới của nó trên console.
8. useLayoutEffect()
Trong React Hook, useLayoutEffect()
là một trong những hook giống như useEffect()
, được sử dụng để thực hiện các tác vụ liên quan đến giao diện người dùng sau khi component được render. Tuy nhiên, sự khác biệt giữa useLayoutEffect()
và useEffect()
là thời điểm mà chúng được gọi trong quá trình render.
useLayoutEffect()
được gọi ngay sau khi React đã thực hiện các thay đổi trên DOM và trước khi các thành phần con của component được render lại. Nó được sử dụng để thực hiện các tác vụ tác động đến giao diện người dùng, chẳng hạn như thay đổi kích thước, vị trí hoặc thuộc tính của phần tử trên trang web.
Bạn có thể sử dụng useLayoutEffect()
để tính toán kích thước của một phần tử DOM sau khi nó được render và thực hiện một số tác vụ dựa trên kích thước đó.
Ví Dụ:
import React, { useLayoutEffect, useRef, useState } from 'react'; function MyComponent() { const [size, setSize] = useState({ width: 0, height: 0 }); const myRef = useRef(null); useLayoutEffect(() => { setSize({ width: myRef.current.clientWidth, height: myRef.current.clientHeight }); }, []); return ( <div ref={myRef}> The size of this element is {size.width} x {size.height} pixels. </div> ); }
Trong ví dụ này, chúng ta sử dụng useLayoutEffect()
để tính toán kích thước của phần tử được trỏ đến bởi myRef, sau đó cập nhật giá trị của size thông qua hàm setSize()
. Cuối cùng, chúng ta hiển thị giá trị của size trên trang web.
Lưu ý rằng việc sử dụng useLayoutEffect()
có thể làm chậm quá trình render, do đó nên sử dụng nó cẩn thận và chỉ khi thực sự cần thiết. Nếu không có tác vụ tác động đến giao diện người dùng, hãy sử dụng useEffect()
thay vì useLayoutEffect()
Top 3 câu hỏi hay nhất về React Hook trong React Js:
1. Hook là gì và tại sao nó được sử dụng trong React?
2. So sánh useState và useReducer Hook trong React, và khi nào nên sử dụng mỗi loại?
3. Làm thế nào để tạo một custom Hook trong React
III. Kết Luận:
React Hook là một tính năng quan trọng của React giúp cho việc quản lý state và side effect trở nên dễ dàng và đơn giản hơn. Việc sử dụng Hook không chỉ giúp cho code gọn hơn mà còn giúp cho việc kiểm thử và tái sử dụng code trở nên thuận tiện hơn. Tuy nhiên, việc sử dụng Hook cần có kiến thức nền tảng về React và các nguyên tắc của Hook để tránh gặp phải các lỗi phức tạp trong quá trình phát triển ứng dụng.
Tóm lại, React Hook là một tính năng mạnh mẽ của React, giúp cho việc quản lý state và side effect trong ứng dụng trở nên dễ dàng và đơn giản hơn. Việc sử dụng Hook đòi hỏi sự hiểu biết về React và kiến thức cơ bản về Hook để có thể tận dụng được tính năng này một cách hiệu quả.