React Hook -Function Component
Function Component
I.Phân Loại :
Trước tiên thì cần nhận dạng một chút :
- Class Component, Function Componen đều có stateless và stateful. Vậy thuật ngữ stateless và stateful là gì ? Còn Class Component, Function Component là gì thì bạn phải hiểu nó trước rồi nhé.
- stateless - stateful
- Cả 2 liên quan đến trạng thái (state) nội bộ của component.
- stateless component : là component không giữ trạng thái nội bộ (Được xây dựng chỉ dựa trên props được truyền vào và không giữ bất kỳ trạng thái nào trong quá trình chạy).
- stateful component : là component giữ trạng thái nội bộ (Có khả năng thay đổi trạng thái trong quá trình chạy và thường được sử dụng khi cần theo dõi và đáp ứng vào sự thay đổi của trạng thái).
II.Yêu Cầu
- Hiểu về LifeCycle methods của Class Component
- Hiểu cách quản lý trạng thái (state) và thuộc tính (props) trong React.
- Hiểu rõ về cách viết và sử dụng functional components trong React.
- Nắm vững các khái niệm cơ bản của JavaScript như functions, arrays, objects
- Hiểu cách sử dụng các tính năng ES6+ như destructuring, spread/rest operators, và arrow functions.
III.Giới Thiệu
- Hooks chính thức được giới thiệu trong phiên bản React 16.8. Nó cho phép chúng ta sử dụng state và các tính năng khác của React mà không phải dùng đến Class (Class Component).
- Điều này có nghĩa là các phiên bản dưới 16.8 thì muốn sử dụng state phải dùng đến khai báo Class
- Và tử 16.8 trở đi : Để có thể thay thế được Class thì React Hooks cung cấp cho chúng ta một bộ các built-in Hooks, giúp chúng ta sử dụng được các thành phần tạo nên React, có 2 loại built-in đó là: Basic Hooks và Additional Hooks.
IV.Hook có những hook gì hay dùng ? sử dụng như thế nào?
Phần này mình chỉ giới thiệu các hook hay dùng và cách dùng cơ bản. Muốn sử dụng được tất cả hooks và sử dụng cho trường hợp nào thì bắt buộc những hooks cơ bản phải nắm vững trước.
Basic Hooks
1.useState:
const [state, setState] = useState(false);
onClick() {
setState(true)
}
- state : giá trị khởi tạo của nó và ở đây bạn đặt là false.
- setState() : là hàm sẽ thay đổi giá trị của state. và chỉ có hàm này mới có thể thay đổi được giá trị của state.
- useState() : hook này nhận giá trị khởi tạo đầu tiên của biến state. Có thể [ ],{ },null, hoặc một callback (nhớ có return) thực hiện các phép logic phức tạp và không cần chạy lại khi re-render.
- Khi giá trị state bị thay đổi bởi hàm setState() thì component chứa nó sẽ bị re-render.
2.useEffect
- Nó giúp chúng ta xử lý các side effects, useEffect sẽ tương đương với các hàm componentDidMount, componentDidUpdate và componentWillUnMount trong LifeCycle
- Thế nên bạn chỉ cần chú ý đến các dependencies của useEffect(). và hiểu được LifeCycle methods của Class Component
Các Cách để sử dụng useEffect() :
Thực hiện sau mỗi lần render :
useEffect(() => {
// Code thực hiện side effect sau mỗi lần render
Thực hiện side Effect chỉ một lần khi component được Mount :
useEffect(()=>{
// Code thực hiện khi component được Mount
},[])
- Mảng dependency để là mảng rỗng thì giống với life cycle componentDidMount. code chỉ chạy một lần khi component được Mount ( load lại trang chẳng hạn -> sử dụng để gọi api để lấy dữ liệu khi load lại trang ) Thực hiện side Effect khi giá trị Dependency thay đổi
useEffect(() => {
// Code thực hiện side effect khi giá trị dependency thay đổi
}, [dependency]);
- dependency : mảng chứa một hoặc nhiều biến, đối tượng, bất cứ thứ gì để quản lí khi biến hoặc đối tượng nào thay đổi thì sẽ chạy vào code trong useEffect. Cần phải console.log ra kẻo rơi vào vòng lặp vô tận nhé. dependency phải hợp lí và thật cẩn thận với nó. (ComponentDidUpdate) Thực hiện khi component Unmount (Cleanup Function) hoặc Dependency thay đổi.
useEffect(() => {
// Code thực hiện side effect
// Cleanup function
return () => {
// Code thực hiện cleanup khi component unmount hoặc dependency thay đổi
};
}, [dependency]);
-
Hàm có return về một call back : Cleanup function sẽ được gọi khi component unmount hoặc dependency thay đổi (ví dụ: hủy các tài nguyên, subscriptions)
-
Nếu bạn muốn thực hiện cleanup function đúng một lần là component unmount thì để dependency [ ] là được. Thực Hiện Side Effect Asynchronous (ví dụ: Fetch Data):
useEffect(() => {
const fetchData = async () => {
try {
// Thực hiện lấy dữ liệu từ API
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// Xử lý dữ liệu
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchData();
}, []);
Thực Hiện Side Effect Khi Component Update Nhưng Trước Khi Render (useLayoutEffect):
import { useLayoutEffect } from 'react';
useLayoutEffect(() => {
// Code thực hiện side effect trước khi component render
});
Tổng kết useEffect :
- Nhớ rằng useEffect chủ yếu được sử dụng để thực hiện các tác vụ không làm ảnh hưởng đến giao diện người dùng trực tiếp, như làm việc với dữ liệu từ API, đăng nhập, đăng xuất, hoặc thay đổi dữ liệu ở mức độ ứng dụng. Nếu bạn cần làm việc với các tác vụ liên quan đến giao diện người dùng, có thể xem xét sử dụng các hook khác như useState, useReducer.
3.useReducer
- useReducer : Là một React Hook, được sử dụng để quản lý trạng thái của component với logic phức tạp. Nó nhận vào một reducer function và một initial state, và trả về một mảng gồm state hiện tại và một hàm dispatch để gửi các action đến reducer.
- Sử dụng để quản lý trạng thái của component với logic phức tạp.
const [state, dispatch] = useReducer(reducer, initialState);
- initialState : là giá trị ban đầu của state, nghĩa là khi component được render lần đẩu tiên thì state sẽ nhận giá trị này.
- State : là biến mà useReducer trả về , chứa giá trị hiện tại của state khi lần đầu render. Khi state thay đổi, component sẽ được re-render để phản ánh trạng thái mới.
- dispatch : là hàm mà useReducer trả về. Bạn sẽ sử dụng để gửi action đến reduder. Action là gì thì tùy vào dự án bạn đang làm (xóa sản phẩm, thêm sản phẩm,...)
- reducer : hàm bạn tự định nghĩa ra để bắt được action mà bạn gửi dựa vào if else hoặc switch case. Hàm này nhận 2 tham số là state hiện tại và action.
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
So Sánh useState : useState() trả về hàm setState để thay đổi giá trị trực tiếp của state. useReducer() trả về hàm dispatch() . khi dispatch được gọi để đẩy một hành động nào đó của user thì lúc này bạn bắt hành động và thực hiện logic ở hàm reducer.
Ví dụ về useReducer :
import React, { useReducer, useState } from 'react';
// Reducer function
// todoReducer là một reducer phức tạp, xử lý các action như ADD_TODO, TOGGLE_TODO, và DELETE_TODO.
// Nó sẽ tạo mới công việc, đánh dấu công việc đã hoàn thành, và xoá công việc dựa trên action được gửi đến.
const todoReducer = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, { id: Date.now(), text: action.payload, completed: false }];
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;
}
};
// Initial state
const initialTodos = [];
function TodoApp() {
// Sử dụng useReducer
// useReducer được sử dụng để quản lý trạng thái danh sách công việc (todos).
const [todos, dispatch] = useReducer(todoReducer, initialTodos);
// Sử dụng useState để quản lý giá trị nhập của người dùng (newTodo)
const [newTodo, setNewTodo] = useState('');
// addTodo là một hàm được gọi khi người dùng nhấn nút "Add Todo".
// Nếu newTodo không rỗng, một action ADD_TODO sẽ được gửi đến reducer để thêm công việc mới.
const addTodo = () => {
if (newTodo.trim() !== '') {
dispatch({ type: 'ADD_TODO', payload: newTodo });
setNewTodo('');
}
};
return (
<div>
<h1>Todo List</h1>
<input
type="text"
value={newTodo}
onChange={e => setNewTodo(e.target.value)}
placeholder="Enter a new task"
/>
<button onClick={addTodo}>Add Todo</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
onClick={() => dispatch({ type: 'TOGGLE_TODO', payload: todo.id })}
>
{todo.text}
</span>
<button onClick={() => dispatch({ type: 'DELETE_TODO', payload: todo.id })}>
Delete
</button>
</li>
))}
</ul>
</div>
);
}
export default TodoApp;
4.useMemo useCallback
Ôn lại trước đã :
- Một component re-render khi nào ? các component con trong component re-render đó có render lại không ? các function có bị tạo lại không ? Vì thế vấn đề hiệu xuất xảy ra và useMemo và useCallback được react cho ra đời.
useMemo
- useMemo giúp ta kiểm soát được việc render dư thừa của các component con, Vậy nó kiểm soát như nào ? nó giống với thằng nào trong LifeCycle ??
- Nó giống với hàm shouldComponentUpdate trong LifeCycle.
- useMemo thường được sử dụng để lưu giữ giá trị được tính toán và tránh việc tính toán lại mỗi lần render. Và chỉ tính toán lại nếu các dependency đã định nghĩa thay đổi
- useMemo là một Hook của React và nó nhất thiết phải có một hàm callback và trả về một giá trị.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
- () => computeExpensiveValue(a, b) : là hàm callback chứa logic tính toán.
- [a, b] : là một mảng các dependency. useMemo sẽ tính toán lại giá trị khi bất kỳ phần tử nào trong mảng này thay đổi.
useCallback
- Khi một component re-render, các hàm callback thường được tạo lại, và điều này có thể gây ra việc re-render không cần thiết cho các components con sử dụng các hàm callback này. useCallback giúp tránh điều này bằng cách lưu giữ và tái sử dụng hàm callback đã được tạo.
const memoizedCallback = useCallback(
() => {
// Callback function
},
[dependencies]
);
- () => { / Callback function / } : là hàm callback bạn muốn (ghi nhớ) memoize.
Cách Hoạt Động của useMemo và useCallback?
Khi được hỏi tại sao lại sử dụng useMemo và useCallback thì ai cũng biết là tăng hiệu xuất, tối ưu vấn đề re-render,.... Rồi cứ thế vác nó sử dụng và lạm dụng để mong muốn ứng dụng chạy nhanh như cách nghĩ của bản thân.
Cách hoạt động :
- Nếu một giá trị hoặc một hàm được bọc lại bởi một trong 2 hook này, thì React sẽ lưu nó vào bộ nhớ đệm trong lần hiển thị đầu tiên và trả về tham chiếu đến giá trị đã lưu trong các lần hiển thị tiếp theo.
- Nếu không được bọc lại thì các giá trị như array, object, function sẽ được tạo lại từ đầu sau mỗi lần re-render.
Ví dụ :
const a = {name: "A"};
const b = {name: "B"};
console.log(a === b); // false
const c = a;
console.log(a === c); // true
Các bạn thấy khoản so sánh a === b sẽ luôn là false, nhưng khi các bạn khai báo c = a nghĩa là c đang tham chiếu tới a cho nên a === c sẽ là true.
-
Thì cách hoạt động của useMemo và useCallback này cũng y như là c === a.
-
Vậy khi lạm dụng tại sao ứng dụng lại chậm ??:
- Chúng hữu ích cho việc lưu giá trị giữa các lần re-render ngoại trừ lần khởi tạo đầu tiên. React phải xử lí thêm nhiều việc và phải lưu chúng vào bộ nhớ đệm trong lần đầu tiên. Cho nên nếu các bạn sử dụng ít thì không sao nhưng sử dụng nó ở hàng trăm hàng ngàn chỗ thì có thể xảy ra vấn đề ngay.
-
Tiếp đến là cần ghi nhớ điều tưởng chừng ai cũng rõ để tránh đi sự vô nghĩa khi bạn sử dụng useMemo và useCallback : Với useMemo và useCallback cho các props trên Component đó chính là: Khi tất cả các props và chính Component đó đều được ghi nhớ(memoized). Còn không thì tất cả mọi thứ đều vô nghĩa.
-
Tại sao vô nghĩa ???: Để hiểu lý do tại sao, chúng ta cần nắm được một điều quan trọng trong React đó chính là tại sao component trong React có thể re-render.
-
Loại bỏ Khi nào : Các bạn có thể hoàn toàn bỏ useMemo và useCallback khi:
- Các bạn truyền props trực tiếp hoặc vào dependencies(useEffect) cho một Component mà Component đó không được ghi nhớ(Memoized)
- Các bạn truyền props trực tiếp hoặc vào dependencies(useEffect) cho một Component mà Component đó có ít nhất 1 props không được ghi nhớ(memoized)
- Lưu ý: Props là các giá trị như object, array hay function
- Tại sao không tối ưu mà lại đi xóa bỏ chúng? Đơn giản là khi các bạn làm việc nếu các bạn thấy Component của các bạn có vấn đề về hiệu suất thì chắc chắn các bạn sẽ giải quyết nó, đúng chứ ? Nhưng nếu Component của bạn không có 1 bất kỳ vấn đề nào về hiệu suất cả. Vậy thêm useMemo và useCallback để làm gì ?
Kết
- Bài dài quá nên anh thông cảm. Hi vọng tương lai mình có thể chia sẻ các bài viết hay hơn. Cảm ơn tất cả mọi người đã đọc bài viết nhé ^^.
- Còn các hook khác mình sẽ nói tới ở bài tiếp theo.
All rights reserved