State
State là 1 khái niệm trong Class Component, trong Funcitonal Component chúng ta dùng useState
const[state, setState]=useState(initialStateValue);
state
: định nghĩa tên của state nó có thể là đơn giá trị hoặc objectsetState
: định nghĩa tên function dùng cho việc update stateinitialStateValue
: là giá trị ban đầu của state.
code Typescript ta sẽ viết như này:
const[state, setState]= useState<string |undefined>(initialStateValue);
kiểu dữ liệu của State sẽ được khai báo luôn trong lúc tạo state: <string | undefined>
useState dùng để làm gì?
nó dùng để lưu lại giá trị bất kì được sử dụng trong Component
giá trị này sẽ bị kill khi Component này không còn được hiển thị nữa
useState nhận tham số gì?
tham số duy nhất nó nhận là giá trị khởi tạo của state
useState trả về gì?
nó trả về 1 cặp giá trị dưới dạng mảng [state, funciton để thay đổi state]
nếu không muốn update giá trị thì bỏ function đi cũng được:[state]
cách viết này áp dụng destructor của javascript bản mới
lưu ý khi dùng useState:
1. tại sao tôi phải dùng useState mà không khai báo biến bình thường trong Component? let state = initialStateValue; chẳng hạn?
về cơ bản, sử dụng hay không còn tùy vào trường hợp, sử dụng useState
sẽ trả về 1 biến const, nó sẽ chặn phần nào việc thay đổi giá trị của state
việc sử dụng state sẽ cung cấp 1 đối tượng sống xuyên suốt thời gian sống của Component, còn biến thông thường thì không chắc
từng có trường hợp mình dùng let state = initialStateValue
thay thế cho useState
, kết quả là let state
nhận về undifined
cụ thể mình không nhớ, cũng không hiểu tại sao nhưng rõ ràng là 1 bài học
mình thấy dùng useState
vẫn tốt hơn khai báo let state
, kiểu khai báo local này nên xài bên trong function thì hơn
2. tạo initialStateValue bằng callBack function
tại sao phải sử dụng cách này nhỉ?
tại 1 số trường hợp, giá trị khởi tạo không phải dễ tính toán như vậy, nhưng React phải đợi kết quả trả về rồi mới xử lý tiếp, việc này làm cho Project cảm giác như bị đơ vậy. Để tránh trường hợp như vậy có thể xem xét áp dụng trả về 1 callBack của việc tính toán này:
constresult=()=>expensiveComputation();const[state, setState]= useState<string>(result);
Lazy initial state
The initialState argument is the state used during the initial render.
In subsequent renders, it is disregarded.
If the initial state is the result of an expensive computation, you may provide a function instead
3. useState() use REPLACING instead of MERGING
nếu sử dụng setState(newVal) thì giá trị cũ sẽ bị mất đi, giá trị mới sẽ ghi đè lại giát trị cũ
đây sẽ chả phải vấn đề nếu giá trị không phải Object, sẽ có 2 trường hợp xảy ra
- bạn sẽ phải clone 1 Object mới từ Object cũ rồi update các giá trị cần thiết: setPerson({ …person, color: ‘green’ });
- vì state là Object nên có thể đổi giá trị bên trong Object 1 cách bình thường: person.color = ‘green’;
ưu nhược điểm:
- tính chất của setState là update chậm, có nghĩa là:
line 10:setPerson({ ...person, color: 'green' });
line 11:person -> data vẫn chưa được update
nó sẽ còn chưa update cho đến khi tất cả các hàm đang chạy hiện tại chạy hết, trước khi re-gender thì nó mới update
hãy áp dụng cách này nếu data sử dụng để hiển thị trên màn hình, không cần sử dụng ngay khi mới update - việc sửa trực tiếp Object như này không thông qua hàm setState
và dĩ nhiên là nó sẽ không trigger hàm re-gender trên màn hình hay các hook khác như useEffect chẳng hạn
hãy sử dụng nếu muốn lấy giá trị mới để sử dụng luôn, áp dụng nhiều lúc submit chuẩn bị chuyển trang
Unlike the setState method found in class components, useState does not automatically merge update objects.
You can replicate this behavior by combining the function updater form with object spread syntax:
setState(prevState=>{// Object.assign would also workreturn{...prevState,...updatedValues};});
Another option is useReducer, which is more suited for managing state objects that contain multiple sub-values.
4. Cập nhật state sử dụng callback function
Thay vì cập nhật state bằng cách truyền vào giá trị mới, thì chúng ta có thể cập nhật state bằng cách truyền vào một hàm callback (có tham số là giá trị cũ) và trả về kết quả là giá trị mới. Ví dụ:
setCount(prevCount=> prevCount +1)
Dùng cách này thì khi cập nhật state sẽ đảm bảo giá trị mới phụ thuộc vào giá trị cũ chứ không phụ thuộc vào giá trị của state ở thời điểm hiện tại.
Ví dụ:
Nếu cập nhật state bằng cách trên thì khi người dùng bấm nút nhiều lần trong khoảng thời gian 3 giây, thì sau 3 giây giá trị của state cũng chỉ tăng lên 1:
functionhandleClick(){setTimeout(()=>{setCount(count +1);// Thay đổi state dựa theo giá trị của state hiện tại},3000);}
Nếu cập nhật state bằng cách truyền vào một hàm thì trong 3 giây delay, người dùng bấm nút bao nhiêu lần thì giá trị của state sẽ tăng lên bấy nhiêu.
functionhandleClick(){setTimeout(()=>{setCount(prevCount=> prevCount +1);// Thay đổi state dựa theo giá trị của state trước đó},3000);}
Như vậy tùy từng trường hợp mà chúng ta sẽ lựa chọn cách sử dụng sao cho hợp lý.
5. tại sao lại cần có <kiểu dữ liệu> trong useState<kiểu dữ liệu>(initialStateValue)
==> mục này không còn hoạt động tại thời điểm hiện tại, mình code demo thì không còn tái hiện được nữa, nhưng mọi người vẫn nên đọc tham khảo.
giả sử xét đối tượng:
const[messageObj, setMessage]=useState({ message:'', id:1});
onChange={e=>{const newMessageObj ={ message: e.target.value };setMessage(newMessageObj);}}<strong>{messageObj.id}:{messageObj.message}</strong>
sau khi call onChange
, setMessage
được gọi nhưng Object lúc này chỉ chứa message
, và nó vẫn hoạt động bình thường, tất nhiên, id
đã bị mất.
việc khai báo rõ ràng kiểu dữ liệu bên trong sẽ tránh được những lỗi như này
6. tại sao trong 1 useState thì state phải cho vào danh sách phụ thuộc của các Hook khác, nhưng setState thì không?
React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.
hay nói cách khác, state thì có thể thay đổi, chứ hàm setState thì không thay đổi, và React cam kết điều này, nên trong các Dependency List, bạn sẽ phải thêm state để kiểm tra thay đổi chứ không phải là hàm setState
kết luận
- useState dùng để lưu dữ liệu trong suốt thời gian sống của Component
- hàm setState sẽ kích hoạt re-render, nhưng update giá trị chậm, sửa giá trị thông qua ref của Object thì không re-render, nhưng giá trị được sửa ngay lập tức
- initial State và update State đều có thể truyền vào callback function, nhưng nên để ý khi nào dùng, cẩn thận khó maintain
Nguồn: viblo.asia