thắc mắc [React] Component thực thi hàm trong useEffect không đúng mong đợi

  • Người tạo chủ đề Người tạo chủ đề giayvun
  • Ngày bắt đầu Ngày bắt đầu

giayvun

Senior Member
Chào các thím, như em được biết thì các lệnh trong useEffect sẽ được thực thi sau một lần render/re-render. Nhưng có chỗ này em không hiểu sao React lại để dồn useEffect callback của lần re-render thứ hai qua sau khi re-render xong lần thứ ba.
Thím nào hiểu được nguyên nhân hoặc có kiến giải về vấn đề này cho em xin thỉnh giáo, đa tạ rất nhiều

JavaScript:
export default function App() {
  const [mock, setMock] = useState("123");

  useEffect(() => {
    setTimeout(() => {
      setMock("456");
    }, 3000);
  }, []);

  return (
    <div className="App">
      <Test propValue={mock} />
    </div>
  );
}


JavaScript:
export default function Test({ propValue }) {
  const [preValue, setPreValue] = useState(propValue);

  count += 1;
  console.log("Start render:", count);

  useEffect(() => {
    console.log("Effect PROP-Change:", { prop: propValue, state: preValue });
  }, [propValue]);

  useEffect(() => {
    console.log("Effect STATE-Change:", { prop: propValue, state: preValue });
  }, [preValue]);

  useEffect(() => {
    console.log("Effect ON-MOUNT");
  }, []);

  if (preValue !== propValue) {
    console.log("Set state handler");
    setPreValue(propValue);
  }

  console.log("Render:", { prop: propValue, state: preValue });
  console.log("End render:", count);
  console.log("----");

  return <div>{propValue}</div>;
}

Screenshot 2023-10-17 at 21.53.30.png
 
Sửa lần cuối:
Theo mình thì đoạn
JavaScript:
  if (prevValue !== propValue) {
    console.log("Set state handler");
    setPrevValue(propValue);
  }
nó trigger re-render và bỏ qua phần commit, nên mấy cái useEffect ở log 2 chưa chạy vì chưa commit.
 
if (prevValue !== propValue) {
console.log("Set state handler");
setPrevValue(propValue);
}
Chỗ khởi tạo ghi là setPreValue còn ở đây là setPrevValue - có thêm chữ v
 
Theo mình thì đoạn
JavaScript:
  if (prevValue !== propValue) {
    console.log("Set state handler");
    setPrevValue(propValue);
  }
nó trigger re-render và bỏ qua phần commit, nên mấy cái useEffect ở log 2 chưa chạy vì chưa commit.
Commit là console.log hả thím

Nếu nó bỏ qua thì sao nó còn console ở sau lần render thứ 3 thím, trong khi lần 3 chỉ có state change chứ prop ko có change 🤔
 
Sorry thím @daicatuanbeo mình sửa code trước khi up vì dư chữ v ko đẹp
chứ code mình sai biến nó báo lỗi ak, mình test r
p/s: Đã edit sửa lỗi chính tả
Chỗ condition cũng chưa sửa kìa thým

const [preValue, setPreValue] = useState(propValue);
Chỗ này ý của thým là mẶc định khởi tạo preValue=proValue à?
Thế đoạn condition check rồi set lại làm gi?
 
Chỗ condition cũng chưa sửa kìa thým

const [preValue, setPreValue] = useState(propValue);
Chỗ này ý của thým là mẶc định khởi tạo preValue=proValue à?
Thế đoạn condition check rồi set lại làm gi?
này là ý tưởng về "giá trị trước đó" mà không dùng useEffect trong một ví dụ của React official documents. Nhưng mà khi implement thì mình lại thấy có chỗ thắc mắc về lần chạy useEffect của prop change thôi à. Vô tình thấy khi mình muốn diễn giải quá trình chạy cho 1 bạn junior hơn mình
 
Theo em nghĩ chắc là khi nó chạy "setPrevValue(propValue)" ở lần count 2, thì nó sẽ không chạy return() để update UI, mà nó sẽ chạy return() ở lần count 3, lúc này sau khi render nó chạy useEffect "console.log("Effect PROP-Change:", { prop: propValue, state: preValue });" nên phần này preValue đã là 456. Em nghĩ nó muốn tối ưu render UI (cũng như nó sẽ batched khi có nhiều setState), nên return() chắc là sẽ chạy khi setState chạy xong để render lại UI.
 
Theo em nghĩ chắc là khi nó chạy "setPrevValue(propValue)" ở lần count 2, thì nó sẽ không chạy return() để update UI, mà nó sẽ chạy return() ở lần count 3, lúc này sau khi render nó chạy useEffect "console.log("Effect PROP-Change:", { prop: propValue, state: preValue });" nên phần này preValue đã là 456. Em nghĩ nó muốn tối ưu render UI (cũng như nó sẽ batched khi có nhiều setState), nên setState chắc là sau khi chạy xong thì nó mới chạy return để render lại UI.
Cám ơn bạn, Nếu bạn để ý mấy dòng console log thì nó vẫn có chạy tiếp cho đến hết 1 chu kỳ.
"End render 2",
javascript vẫn chỉ là mấy function thôi, chạy tới đây rồi thì ko thể không chạy tiếp lệnh return trong hàm, nên lý do nó ngắt function render mình thấy chưa hợp lý
 
Cám ơn bạn, Nếu bạn để ý mấy dòng console log thì nó vẫn có chạy tiếp cho đến hết 1 chu kỳ.
"End render 2",
javascript vẫn chỉ là mấy function thôi, chạy tới đây rồi thì ko thể không chạy tiếp lệnh return trong hàm, nên lý do nó ngắt function render mình thấy chưa hợp lý
Ý e là, thay vì nó render UI ở lần count 2, thì khi có setState nó sẽ batching lại, để tối ưu render UI, nên nó sẽ render 1 lượt, sau đó nó sẽ chạy useEffect tương ứng, nên lúc này prevValue đã là 456 ý a.
 
Ý e là, thay vì nó render UI ở lần count 2, thì khi có setState nó sẽ batching lại, để tối ưu render UI, nên nó sẽ render 1 lượt, sau đó nó sẽ chạy useEffect tương ứng, nên lúc này prevValue đã là 456 ý a.
Cám ơn idea của bạn, để mình tìm hiểu thêm về batching feature này,
Mình có thử react-17 và react-18 thì nó cùng 1 kết quả
 
Hi @mat.troi.be.con , @Liiibra
Mình có thử theo ví dụ trong documents của react thì đúng là gọi nhiều lần thì nó batching lại 1 lần re-render sau cùng, và ko chạy code render những lần giữa giữa.

Nó có hơi khác với re-render batching, theo 1 anh người nước ngoài giải thích thì trong quá trình re-render nếu có 1 update gì đó phải re-render tiếp thì nó lại tiếp tục re-render trước khi thực hiện hàng đợi useEfffects,

In this case it's just because of the setState. Since you are setting the new state, React knows that the component will need to render another time, so it goes on and updates the component's state before it starts to run any effects. The effects then run after the component has reached the state it aimed for.

If React wouldn't have waited for the state update to render, the useEffect would have triggered even more times, especially for complex components.

Mình có thể tạm hiểu như vậy, nhưng chưa thấy document official ak
 
Cái này rất dễ hiểu, bạn chỉ cần đặt debugger và jump vào react source là sẽ biết rõ nguyên nhân.
 
Hi @mat.troi.be.con , @Liiibra
Mình có thử theo ví dụ trong documents của react thì đúng là gọi nhiều lần thì nó batching lại 1 lần re-render sau cùng, và ko chạy code render những lần giữa giữa.

Nó có hơi khác với re-render batching, theo 1 anh người nước ngoài giải thích thì trong quá trình re-render nếu có 1 update gì đó phải re-render tiếp thì nó lại tiếp tục re-render trước khi thực hiện hàng đợi useEfffects,



Mình có thể tạm hiểu như vậy, nhưng chưa thấy document official ak
doc nó ghi không batch trong trường hợp là event thôi thím, có thể nhầm với cái này
1697566682279.png
 
Có thể hiểu ngắn gọn là do thằng setPreValue(propValue) nó làm cho mấy cái useEffect nó bị deferred (và do react chưa có commit để re-render = useEffect), hay nói cách khác react có thể trì hoãn mấy cái useEffect để batch tụi nó lại cho đỡ phải render nhiều lần. Theo em hiểu là vậy
Cám ơn bạn nhé, mình đã hiểu rõ rồi 😉
 
doc nó ghi không batch trong trường hợp là event thôi thím, có thể nhầm với cái nàyXem tệp đính kèm 2132759
Hi bạn, để mình nói rõ hơn
  • trường hợp batching trong document làm mình trigger update nhiều lần 1 lúc
  • Còn trường hợp mình đang thắc mắc là trong quá trình render nó bị update state 1 lần nữa.
Và mình đang thắc mắc cái trường hợp 2 ak, giờ mình rõ lý do rồi

Cám ơn bạn nhé
 

Thống kê chủ đề

Ngày tạo
giayvun,
Người trả lời cuối
giayvun,
Trả lời
19
Lượt xem
3.174
Quay lại
Lên đầu trang