Vì sao dữ liệu trong state không được update

Mọi người giúp mình bài toán này với, mình đang làm thử chức năng load-more sử dụng useReduceruseEffect của react-hooks .

Link demo: https://codesandbox.io/s/load-more-pmr3t?file=/src/ScrollLoadMore.js:1258-1270

Lúc đầu, mình thực hiện handleFetchUsers thành công và có set giá trị cho biến totalPages ở trong reducer. Nhưng khi ở hàm handleScroll mình lấy biến totalPages ra để check điều kiện thì nó lại không đúng với giá trị mà mình đã set trước đó trong reducer.

Mọi người có thể giải thích thêm giúp mình được không. Mình xin cám ơn các bạn !

Vì dữ liệu của bạn bị closure mất rồi.
Khi totalPages thay đổi thì hàm handleScroll được khởi tạo lại với giá trị mới. Tuy nhiên hàm handleScroll cũ vẫn còn đó và nắm giá trị cũ -> bạn in ra giá trị cũ

  useEffect(() => {
    handleFetchUsers();
    window.addEventListener("scroll", handleScroll);

    return () => window.removeEventListener("scroll", handleScroll);
  }, [totalPages]);

Fix thì như trên

Hoặc tối ưu hơn

  const handleScroll = useCallback(() => {
    console.log("totalPages >>>", totalPages);
    if (page >= totalPages) return;
  }, [totalPages]);

  useEffect(() => {
    handleFetchUsers();
    window.addEventListener("scroll", handleScroll);

    return () => window.removeEventListener("scroll", handleScroll);
  }, [handleScroll]);
5 Likes

Riêng về code thì bạn đã sai khi khai báo listener trong useEffect nhưng lại không remove nó đi

Nói về bug thì để đơn giản, như bạn đã biết, useEffect chỉ run khi mảng dependencies thay đổi, giờ bạn để rỗng, tức là chỉ run 1 lần. Đây là mấu chốt của vấn đề.
useEffect run sau khi render lần đầu tiên, tức là run với giá trị initialState, totalPages = 0. Đây là giá trị bạn log ra khi gọi handleScroll, bạn cứ thử thay initialState thành giá trị khác để kiểm chứng.

Để trả lời câu hỏi “Vì sao handleScroll không cập nhật giá trị theo state?” Thì bạn cần có cái nhìn tổng quát, đúng và chính xác hơn về useEffect. Bạn nên đọc kĩ bài này https://overreacted.io/a-complete-guide-to-useeffect/

4 Likes

Câu này mình vẫn chưa thực sự hiểu, mong bạn có thể giải thích thêm giúp mình được không.
Mình xin cám ơn bạn nhiều

useEffect nó chỉ run một lần ngay sau khi render() lần đầu tiên -> cái đó mình hiểu, và trong useEffect mình chỉ gắn sự kiện sroll cho window thôi, và mỗi lần window scroll sẽ gọi vào hàm handleScroll và lúc này nó lấy biến totalPages từ trong state thì có liên quan gì đến cái initialState đâu nhỉ ?
Mình chưa thực sự rõ chỗ này ?

Đâu tiên, bạn lấy biến totalPage ra từ state

const { loading, users, page, totalPages, error } = state;

Vì totalPage không phải object hay array nên JS ưu ái copy giá rị của totalPages từ state vào biến totalPages của local luôn.
-> khi totalPage update thì phải cần được copy lại

Tuy nhiên, thân phận totalPage do là một biến local yếu đuối. Nên khi ra khỏi scope của hàm ScrollLoadMore thì nó cũng tan thành mây khói.
-> Khi hàm ScrollLoadMore mà chạy xong thì biến totalPage cũng bay màu theo.

Nhưng ngặt nỗi, thằng handleScroll lại gian díu với biến localPage. Mà thằng handleScroll này lại là có máu mặt, chưa kể nó còn được trọng dụng bởi cụ window với event “scroll” nữa. Nên không trảm nó được. Khổ cái nữa là thằng này sống dựa dẫm vào localPage. Bây giờ con localPage chết là thằng kia cũng tự tử luôn.
-> Nếu vậy thì mỗi khi scroll là ko gọi được hàm handleScroll -> Bị lỗi đỏ lòm, chương trình la làng lên. Người dùng bối rối -> không ai thèm vào website của bạn nữa -> bạn thất bại -> bạn chán đời -> bạn suy nghĩ về những thứ tiêu cực

Thế thì làm sao bây giờ? Thì ông chú JS mới nghĩ ra thuật ngữ closure. Tức là một phép màu giúp một hàm, vẫn truy cập được giá trị của 1 biến. Cho dù biến đó đã ra khỏi scope. Phép màu như thế nào thì chúng ta không nên quan tâm. Chỉ biết vậy là đủ


Thì, chương trình của bạn khi nó render xong nó thoát khỏi hàm. Khi này giá trị của totalPage ở trong hàm handleScroll như một bản sao trước đó. Không phải là biến đó nữa. Mà bản sao thì làm sao được update như bản chính. Giống như mua đồ fake thì sao mà được bảo hành chính hãng. Thế là hàm handleScroll vẫn giữ hoài hình bóng đó. Dẫn tới việc in ra bị sai.

5 Likes

Cám ơn bạn nhiều lắm. Chắc bạn đã cố giải thích theo cách đơn giản nhất cho mình dễ hiểu nhưng chắc mình phải dành thời gian tìm hiểu kỹ hơn chứ xem ra mình vẫn còn lơ ngơ chỗ này lắm

1 Like
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?