Trong bài viết lần này, chúng ta hãy cùng tìm hiểu 1 hook mới trong ReactJS là useRef nhé!😄
Vậy useRef được sử dụng khi nào? Và mục đích sử dụng của hook này là gì?
Đầu tiên mình sẽ xây dựng 1 ứng dụng đếm ngược đơn giản như sau:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
import {useState, useEffect, useRef} from "react";
function Content() {
const [number, setNumber] = useState(9000)
const handle_start = () => {
setInterval(() => {
setNumber(preNumber => preNumber - 1)
},100)
}
const handle_stop = () => {
}
return (
<div>
<div style={
{'fontSize': '100px'}
}>
{number}
</div>
<div>
<button
onClick={handle_start}
>Start</button>
<button
onClick={handle_stop}
>Stop</button>
</div>
</div>
)
}
export default Content;
|
Giao diện đơn giản nó sẽ như thế này 😄
Bài toán mình sẽ đi giải quyết ở đây là sau khi bấm nút stop thì ứng dụng đếm số sẽ ngừng lại tại con số hiện thời. Chúng ta đã setInterval ở button start,
vậy để stop chúng ta chỉ cần clearInterval ở button stop thôi. Nhưng function clearInterval() nhận đối số truyền vào là ID hiện thời
Cách đơn giản là chúng ta sẽ thêm 1 biến là timeID và truyền cho giá trị mỗi khi setState lại như sau:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
import {useState, useEffect, useRef} from "react";
function Content() {
const [number, setNumber] = useState(9000)
let timeId
const handle_start = () => {
setInterval(() => {
timeId = setNumber(preNumber => preNumber - 1)
},100)
console.log('start', timeId)
}
const handle_stop = () => {
clearInterval(timeId)
console.log('stop', timeId)
}
return (
<div>
<div style={
{'fontSize': '100px'}
}>
{number}
</div>
<div>
<button
onClick={handle_start}
>Start</button>
<button
onClick={handle_stop}
>Stop</button>
</div>
</div>
)
}
export default Content;
|
Nhưng bài toán vẫn chưa được giải quyết? Chúng ta thử xem log timeID hiển thị trước và sau thế nào nhé!
Vậy là biến timeID không được lưu lại? Tại sao lại như vậy? Đó là bởi vì biến timeID của chúng ta nằm trong component và mỗi khi state thay đổi component
sẽ render lại dẫn đến biến *** timeID*** sẽ được set lại giá trị lại.
Để giải quyết định vấn đề này chúng ta có thể cho biến timeID ra ngoài function component, khi đó, bài toán của chúng ta sẽ được giải quyết
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
import {useState, useEffect, useRef} from "react";
let timeId
function Content() {
const [number, setNumber] = useState(9000)
const handle_start = () => {
timeId = setInterval(() => {
setNumber(preNumber => preNumber - 1)
},1000)
console.log('start', timeId)
}
const handle_stop = () => {
clearInterval(timeId)
console.log('stop', timeId)
}
return (
<div>
<div style={
{'fontSize': '100px'}
}>
{number}
</div>
<div>
<button
onClick={handle_start}
>Start</button>
<button
onClick={handle_stop}
>Stop</button>
</div>
</div>
)
}
export default Content;
|
Tuy nhiên với ReactJS chúng ta không nên làm thế 😄 có nhiều nguyên nhân……nên ở đây chúng ta sẽ sử dụng useRef.
Khi chúng ta dùng useRef để lưu giá trị, thì khi phải render lại một function component, giá trị đã lưu sẽ không bị thay đổi, mà vẫn giữ được giá trị đã lưu trước đó.
- useRef(initialValue) chấp nhận đối số là giá trị đầu vào và trả về một tham chiếu. Tham chiếu là một object đặc biệt có 1 thuộc tính là current: reference.current
- Có 2 điều cần nhớ về useRef là:
- Gía trị của reference sẽ được giữ nguyên khi component re-render
- Việc update reference sẽ không làm cho component re-render
- Tới đây có thể thấy useRef() khá giống với state trong việc save 1 value, nhưng điểm khác nhau là khi set lại state thì
component re-render, còn với useRef() thì không. Một điểm nữa là state update sẽ chạy bất đồng bộ, còn với useRef() sẽ update ngay lập tức vì được chạy đồng bộ.
Mình sẽ sửa lại code sử dụng useRef như sau:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
import {useState, useEffect, useRef} from "react";
function Content() {
const [number, setNumber] = useState(9000)
const ref = useRef(99)
let timeId = useRef(99)
const handle_start = () => {
timeId.current = setInterval(() => {
setNumber(prevNumber => prevNumber - 1)
}, 1000)
console.log('start', timeId.current)
}
const handle_stop = () => {
clearInterval(timeId.current)
console.log('stop', timeId.current)
}
return (
<div>
<div style={
{'fontSize': '100px'}
}>
{number}
</div>
<div>
<button
onClick={handle_start}
>Start</button>
<button
onClick={handle_stop}
>Stop</button>
</div>
</div>
)
}
export default Content;
|
Lưu ý: Hạn chế việc sử dụng useRef mà thay vào đó nên sử dụng state, chỉ khi không thể sử dụng được state thì mới dùng useRef(), như trường hợp tuy cập DOM.
Hi vọng bài viết sẽ giúp ích được các bạn hiểu rõ hơn khi nào thì sử dụng nó và áp dụng vào từng trường hợp cụ thể. Nếu có bất kì thắc mắc hay góp ý nào xin comment phía dưới. Mình xin cảm ơn!