Các dự án React gần đây, bạn đã chuyển sang sử dụng Hook thay thế cho Class chưa? Cảm nhận của bạn về React Hook như thế nào? Tuyệt vời phải không!
Mình đã có một số bài viết giới thiệu về React Hook, nếu bạn chưa rõ về khái niệm này thì có thể tham khảo lại nhé.
Trong các dự án React mình tham gia, mình hay sử dụng các hook như useEffect, useSelector… Trong đó, có một hook mình ít khi dùng, nhưng trong một số trường hợp, nó lại vô cùng hữu dụng. Đó là hook useCallback.
Khi nào thì nên sử dụng useCallback
?
Bài viết này mình sẽ không đưa ra câu trả trả lời cụ thể, mà sẽ giải thích chi tiết ý nghĩa và công dụng của hook này. Sau đó, việc dùng nó hay không là sự lựa chọn của bạn.
Chúng ta vào việc thôi nhỉ!
Nội dung chính của bài viết
Ví dụ bài toán sử dụng useCallback
Mình sẽ đặt một bài toán đơn giản như sau: Chúng ta có một màn hình có hai component lồng nhau:
- Parent
- Child
Trong đó, Parent component có một button để tăng giá trị state (kiểu như một bộ đếm vậy). Còn Child thì đơn giản là hiển thị một đoạn text và không làm gì cả.
Dưới đây là mã nguồn hai component đó:
import React from "react"; const Child = ({ reset }) => { console.log("re-render child component.") return ( <div> <p>child component which has nothing to do with parent count</p> </div> ); }; export default Child;
và Parent Component:
import React, { useState } from 'react'; import { render } from 'react-dom'; import Child from "./Child"; import './style.css'; const Parent = () => { const [count, setCount] = useState(0); console.log("re-render parent component"); return ( <main> <p>Count: {count}</p> <button onClick={() => setCount(count=>(count+1))}>Increment</button> <Child /> </main> ) } render(<Parent />, document.getElementById('root'));
Giao diện chương trình:
Khi mỗi lần click vào button Increment, bạn sẽ thấy console log hiển thị 2 log như sau:
re-render parent component re-render child component.
Bạn có nhận ra vấn đề ở đâu không?
Mặc dù Child component không hề thay đồi trạng thái state, nhưng nó cũng bị re-render lại. Vậy để tránh Child component bị re-render lại, mình sử dụng React.memo()
để giải quyết.
// Child.js import React, { memo } from "react"; const Child = memo(({ reset }) => { // same content as earlier });
Ok, sau đó thử kiểm tra lại kết quả, đúng là Child component không còn bị re-render nữa phải không?
re-render parent component
Giờ chúng ta thêm một yêu cầu: Đặt nút Reset bộ đếm vào trong Child component (thay vì đặt trong Parent component, chúng ta cứ thử làm khó mình một chút nhé).
Lúc này, mã nguồn của Child.js sẽ đổi lại như sau:
// Child.js import React, { memo } from "react"; const Child = memo(({ reset }) => { console.log("re-render child component.") return ( <div> <p>child component which resets count</p> <button onClick={reset}>Reset Count</button> </div> ); }); export default Child;
Sau đó, Parent component phải đón event reset từ Child và tiến hành cập nhật lại count state
// Parent.js const Parent () => { const [count, setCount] = useState(0); console.log("re-render parent component"); const resetCount = () => { setCount(0); }; return ( <main> <p>Count: {count}</p> <button onClick={() => setCount(count=>(count+1))}>Increment</button> <Child reset={resetCount} /> </main> ) }
Đến lúc này, vấn đề lại phát sinh nè. Khi bạn click vào nút reset thì Child component lại bị re-render – mặc dù bản thân Child component không hề có state bị thay đổi. Như vậy là React.memo
không còn “phép thuật” nữa.
Giờ chúng ta phải làm sao đây?
Sử dụng useCallback để tránh re-render không cần thiết
Để giải quyết bài toán trên, chúng ta sử dụng useCallback
xem sao.
Chúng ta cần đảm bảo hàm resetCount
không được tạo lại mỗi khi Parent render. Đây chính xác là công dụng của useCallback
rồi.
Sửa lại hàm resetCount
trong Parent component:
// Parent.js const resetCount = useCallback(() => { setCount(0); }, [setCount]);
Như vậy, useCallback
sẽ chỉ trả lại cùng một instance của function mỗi khi component bị re-render và nó chỉ tạo lại khi các dependencies thay đổi. Mà dependencies ở đây chính là tham số thứ 2 được bạn truyền vào. Trong ví dụ này là hàm setCount.
Trên đây là một ví dụ minh họa cách sử dụng cũng như khi nào thì cần tới useCallback hook.
Mình hi vọng rằng, với ví dụ này bạn sẽ hiểu rõ thêm về hook đầy bí ẩn này.
💦 Đọc thêm về React:
Bình luận. Cùng nhau thảo luận nhé!