Junior Developer thường mắc 3 lỗi React Function Component State này

17 tháng 08, 2020 - 2139 lượt xem

1. Chỉnh sửa State trực tiếp 

Khi thay đổi một state của component, điều quan trọng là bạn phải return lại một bản sao của state với các sửa đổi đó - không chỉnh sửa trực tiếp đối với state hiện tại. Nếu bạn không chỉnh sửa chính xác state của component, các thuật toán Driffing của React sẽ không bắt được những thay đổi đó và component sẽ không được update đúng cách. 

Ví dụ, bạn có một state như thế này: 

const initialState = ['red', 'blue', 'green']
let [colors] = useState(initialState)

Bây giờ bạn muốn thêm color yellow vào array này, bạn sẽ làm thế này: 

colors.push('yellow')

Hoặc như thế này: 

colors = [...colors, 'yellow']

Nhưng cả hai cách làm trên đều không đúng. Khi update state trong một function component, luôn phải sử dụng phương thức setter của useState hook và phải luôn cẩn thận để không thay đổi các object. Phương thức setter là element thứ hai trong mảng mà useState trả về do đó nó có thể xóa giống như giá trị State. 

Cách đúng thêm element vào mảng:

// Khởi tạo
const initialState = ['red', 'blue', 'green']
const [colors, setColors] = useState(initialState)

// Sau đó, chỉnh sửa state
setColors(colors => [...colors, 'yellow'])

Từ đây chúng ta lại có thêm lỗi thứ 2.

2. Cài đặt State liên quan đến State trước đó mà không sử dụng Function

Có 2 cách dùng phương thức setter được trả về bởi useState Hook. Cách đầu tiên là cung cấp một giá trị (value) mới để làm đối số. Cách thứ hai là cung cấp một hàm (function) mới để làm đối số. Vậy khi nào thì bạn sử dụng cách này thay vì cách kia ? 

Ví dụ: Nếu cần phải có một button có thể bật hoặc tắt được, bạn phải có một phần của State gọi là isDisabled chứa giá trị boolean. Nếu bạn muốn chuyển button này từ bật thành tắt thì có thể viết một cái gì đó giống như thế này, sử dụng giá trị để làm đối số: 

// Khởi tạo
const [isDisabled, setIsDisabled] = useState(false)

// Sau đó, chỉnh sửa state
setIsDisabled(!isDisabled)

Vậy điều này thì có gì sai chứ? Vấn đề nằm ở chỗ là các bản cập nhật state của React có thể được cập nhật theo đợt, nghĩa là nhiều bản cập nhật State có thể diễn ra trong một cycle cập nhật duy nhất.  Nếu các bản cập nhật ấy được cập nhật theo đợt và bạn lại có nhiều bản cập nhật ở State bật/tắt, thì kết quả cuối cùng có thể không như bạn mong đợi.

Cách tốt hơn để cập nhật State ở đây là cung cấp một hàm của State trước đó làm đối số:

// Khởi tạo
const [isDisabled, setIsDisabled] = useState(false)

// Sau đó, chỉnh sửa state
setIsDisabled(isDisabled => !isDisabled)

Giờ đây, ngay cả khi các bản cập nhật State được thực hiện theo đợt và nhiều bản cập nhật cho state bật/tắt thực hiện cùng nhau thì mỗi bản cập nhật sẽ dựa trên state chính xác trước đó để luôn cho ra kết quả mong muốn. 

Điều này cũng đúng với những thứ như tăng bộ đếm.

Không làm thế này: 

// Khởi tạo
const [counterValue, setCounterValue] = useState(0)

// Sau đó, chỉnh sửa state
setCounterValue(counterValue + 1)

Hãy làm thế này: 

// Khởi tạo
const [counterValue, setCounterValue] = useState(0)

// Sau đó, chỉnh sửa state
setCounterValue(counterValue => counterValue + 1)

Chìa khóa ở đây là nếu State mới dựa trên giá trị của State cũ, hãy nên luôn sử dụng một hàm làm đối số. Nếu bạn đang đặt một giá trị không dựa vào giá trị của State cũ thì bạn có thể sử dụng một giá trị làm đối số.

3. Quên rằng phương thức setter từ useState là bất đồng bộ

Cuối cùng, điều quan trọng cần nhớ là phương thức setter được trả về bởi useState Hook là một phương thức không đồng bộ. Ví dụ: tưởng tượng chúng ta có một component có state như sau:

const [name, setName] = useState('Techmaster')

Sau đó chúng ta có một cách có thể cập nhật và ghi state vào console:

const setNameToMatt = () => {
  setName('Techmaster Vietnam')
  console.log(`The name is now... ${name}!`)
}

Có thể bạn sẽ nghĩ nó sẽ ghi 'Techmaster Vietnam' vào console, nhưng không phải, nó lại ghi là 'Techmaster' 😂

Lý do là phương thức setter từ useState Hook là bất đồng bộ. Nghĩa là nó sẽ bắt đầu cập nhật State khi đến dòng gọi setName, nhưng đoạn code bên dưới vẫn tiếp tục thực hiện vì code bất đồng bộ không bị chặn. 

Nếu bạn có code cần chạy sau khi update state, React cung cấp useState hook cho phép bạn viết code chạy sau khi cập nhật bất kỳ một phần phụ thuộc được chỉ định nào

Lưu ý:

Điều này hơi khác với cách thực hiện với một function gọi lại được cung cấp cho phương thức setState trong một class component. Đối với bất kỳ lý do gì đi nữa thì, useState hook đều không hỗ trợ cùng một API, vì vậy các function gọi lại không hoạt động.

Cách đúng để ghi current state sau khi update: 

useEffect(() => {
  if (name !== 'Techmaster') {
    console.log(`The name is now... ${name}!`)
  }
}, [name])

const setNameToMatt = () => setName('Techmaster Vietnam')

Tốt hơn nhiều rồi đấy! Bây giờ nó đã 'Techmaster Vietnam' đúng như mong đợi! 

Bài viết được dịch từ đây, hi vọng bài viết này hữu ích với bạn. 

Lớp ReactJS tại Techmaster Vietnam: https://techmaster.vn/khoa-hoc/25518/web-frontend-nang-cao-voi-react

 

Bình luận

avatar
Trịnh Minh Cường 2020-08-17 15:16:17.778434 +0000 UTC
Đúng cái mình cần, cảm ơn em Thuý nhé
Avatar
avatar
Trịnh Minh Cường 2020-08-17 15:16:23.980157 +0000 UTC
Đúng cái mình cần, cảm ơn em Thuý nhé
Avatar
avatar
Nguyễn Hàn Duy 2020-08-17 15:31:02.753525 +0000 UTC
+1
Avatar
* Vui lòng trước khi bình luận.
Ảnh đại diện
  +20 Thích
+20