Vì được yêu thích nên hôm nay lại là chủ đề về hooks thôi, hy vọng không ai thất vọng với điều này.

useEffect(effect, deps)

Khái niệm

Bằng một vài cách nào đó, bạn có thể tìm được khái niệm useEffect là:

The useEffect Hook allows you to perform side effects in your components.

Chức năng

Khái niệm thật sự khó hiểu đúng không nào, để nói thì useEffect thật sự sẽ thay thế cho componentDidMount, componentDidUpdate, và componentWillUnmount.

Ví dụ

Vấn với ví dụ thường ngày vì mình quá lười =)))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { useState, useEffect, useRef } from "react";

export default function App() {
const [randomID1, setRandomID1] = useState(0);
const [randomID2, setRandomID2] = useState(0);
const ref = useRef();
useEffect(() => {
ref.current1 = randomID1
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button onClick={() => setRandomID1(Math.round(Math.random() * 10))}>
setRandomID1
</button>
<button onClick={() => setRandomID2(Math.round(Math.random() * 10))}>
setRandomID2
</button>
<div>randomID1: {randomID1}</div>
<div>randomID2: {randomID2}</div>
<div>ref: {ref.current}</div>
</div>
);
}

componentDidMount

Đây là cách dùng đơn giản nhất của useEffect với deps của chúng ta là [], khi này thì useEffect của chúng ta chỉ chạy đúng một lần khi did mount:

1
2
3
useEffect(() => {
ref.current = randomID1
}, []);

https://s8.gifyu.com/images/CPT2206211601-786x214.gif

Như bạn thấy, ở ví dụ thì dù bạn có bấm random để re-render bao nhiêu lần đi nữa thì ref.current mà nó chỉ lấy giá trị lúc mount của randomID1 là 1 mà thôi, cũng không thay đổi một lần nào nữa vì lúc này useEffect không chạy khi update mà chỉ chạy khi mount.

componentDidUpdate

Khi chúng ta không truyền tham số vào, tức là lúc này deps ở đây của chúng ta có giá trị là undefineduseEffect sẽ hoạt động như là componentDidUpdate, sẽ chạy sau bất cứ khi nào component re-render (did update)

1
2
3
useEffect(() => {
ref.current = randomID1
});

Hoặc truyền vào state mà bạn muốn nó chạy mỗi khi thay đổi giống như hình dưới.

1
2
3
useEffect(() => {
ref.current = randomID1
}, [randomID1]);

https://s8.gifyu.com/images/CPT2206211604-786x214.gif

Như bạn thấy, mỗi khi mình bấm setRandomID1 thì nó đều thay đổi vì current luôn liên tục được set giá trị của randomID1.

componentDidMount(effect => cleanup, deps)

1
2
3
useEffect(() => {
return () => { console.log('componentUnMount')}
}, []);

bạn chỉ cần return về một function và nó sẽ trở thành componentUnMount rồi, mình không thể cap hình vì mình quá lười nhưng bạn có thể xem nó ở đây nè: https://stackoverflow.com/a/55020668

useLayoutEffect(effect, deps)

Hehe, useEffect là một cái gì đó mà rất thông thường rồi nên mình rất lười khi viết nó, useLayoutEffect lại là một vấn đề khác, cách sử dụng của nó khá tương tự với useEffect và mọi người thường hay tự hỏi khi đọc được nó là nó xuất hiện để làm gì =))

Khái niệm

Bằng một vài bước google thì trang chủ nó có khái niệm là:

The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.
Prefer the standard useEffect when possible to avoid blocking visual updates.

Chức năng

Tương tự useEffect thì useLayoutEffect lấy một function được gọi là effect làm đối số đầu tiên, và một mảng phụ thuộc làm đối số thứ hai. Đối số đầu tiên, effect, trả về một cleanup function hoặc giá trị undefined . useLayoutEffect được minh hoạ như code dưới đây

Ví dụ

Giống hệt như trên luôn, chỉ khá là các bạn ctrl + H và replace useEffect sang useLayoutEffect và mọi thứ hoạt động hoàn hảo không sai một tí nào, và… nó xuất hiện làm gì nhỉ?

So sánh giữa useEffect với useLayoutEffect

Tất nhiên team facebook không rảnh để tạo ra một function mới có chức năng giống hệt mà chỉ đổi tên thôi đúng không nào, useEffectuseLayoutEffect có 2 điểm khác nhau lớn nhất và chí mạng là:

1. Thời điểm thực thi effect:

Với useEffect, thì trình tự thực thi sẽ là:

  1. Bạn gây ra một sự kiện re-render.
  2. React re-render component của bạn.
  3. Màn hình được cập nhật giao diện.
  4. Cuối cùng useEffect chạy một cách bất đồng bộ.

Còn với useLayoutEffect thì:

  1. Bạn gây ra một sự kiện re-render.
  2. React re-render component của bạn.
  3. Cuối cùng useLayoutEffect chạy đồng bộ, khi nào xong thì step tiếp mới được thực thi.
  4. Màn hình được cập nhật giao diện.

2. Bất đồng bộ và đồng bộ:

Bạn thấy ngay keyword rồi đấy, useLayoutEffect sẽ chỉ được thực thi đồng bộ và khi nào nó thực thi xong UI mới được cập nhật đúng không nào. Điều này khiến cho trong một số trường hợp nó giải quyết được vấn đề giá trị state thay đổi khiến cho dữ liệu màn hình nhấp nháy rất khó chịu. Và thường thì nó sinh ra chỉ để giải quyết vấn đề này. Bạn xem gif bên dưới để hiểu thêm nhé

Với useEffect:

https://s8.gifyu.com/images/CPT2206211658-786x214.gif

Bạn có thấy mấy số random nó nháy nháy từ giá trị default sang giá trị mới rất khó chịu đúng không nào.

Chỉ cần ctrl + H và replace useEffect sang useLayoutEffect thì:

https://s8.gifyu.com/images/CPT2206211700-786x214.gif

Cảm giác nhấp nháy khó chịu đó đã biến mất hoàn toàn, thay vào đó thì tốc độ hình ảnh xuất hiện có chậm hơn một tí nhưng mình nghĩ đây không phải vấn đề trong trường hợp này.

Kết luận

Aizz, nếu có ai đó hỏi mình là nên dùng useEffect hay useLayoutEffect thì phần lớn câu trả lời của mình là useEffect, bởi vì useLayoutEffect là đồng bộ và app của bạn sẽ không hiển thị gì cả đến khi effect hoàn thành và nó có thể và chắc chắn có thể là nguyên nhân xảy ra vấn đề về performance nếu bạn có những đoạn code xử lý chậm trong effect.