Bug ini cukup membingungkan:
https://twitter.com/kentcdodds/status/1102659818660102145
Saya pikir itu terjadi karena fn
dijadwalkan oleh setInterval(fn, 0)
melompat di depan [running]
efek pembersihan yang disebabkan oleh setRunning(false)
. Jadi interval masih menyala, menimpa setLapse(0)
yang terjadi selama acara dengan setLapse(someValue)
.
Ini mengingatkan saya pada masalah yang dijelaskan di https://github.com/facebook/react/issues/14750#issuecomment -460409609, atau setidaknya sebagian darinya:
Faktanya, masalah ini ada bahkan untuk penekanan tombol React biasa (dan acara "diskrit" lainnya). Solusi untuk itu adalah menghilangkan efek pasif sebelum kita mendapatkan peristiwa diskrit.
Tapi di sini, sepertinya ini tidak akan cukup karena efeknya terbalik sebagai akibat dari klik, bukan sebelumnya. Jadi haruskah setState
di dalam event diskrit juga menyiram efek pasif? Sepertinya tidak. (Itu akan mengalahkan tujuan menunda mereka.)
Jadi ini berfungsi seperti yang dirancang, dan perbaikannya hanya useLayoutEffect
ketika waktunya penting? Atau solusi rAF?
Masalahnya adalah bahwa pengatur waktu itu sendiri bukanlah peristiwa yang terpisah. Peristiwa diskrit hanya dijamin dalam kaitannya dengan peristiwa diskrit lainnya. Saya tidak berpikir itu yang Anda inginkan di sini.
Menghentikan timer adalah operasi asinkron karena semua status yang ditetapkan adalah asinkron. Jadi sesuatu yang lain bisa masuk sebelum memerah.
Ini adalah kasus di mana useLayoutEffect sebenarnya tidak sepenuhnya memperbaiki masalah. Tentu saja tidak dalam mode bersamaan, tetapi ini juga tidak jelas dalam mode sinkronisasi. Jika itu bukan pengatur waktu tetapi katakanlah acara fokus, maka itu dapat menyala dalam batch sebelum ini memerah yang akan memiliki masalah yang sama.
setRunning(false); // I would like to add a stop of this timer to the queue to be performed later
setLapse(0); // I would like to add an operation to set lapse to zero later
// lots of random stuff that can happen before the batch flushes
// This might also queue an operation to set lapse to something else
// actual rendering
// If concurrent mode, lots of other random stuff that can happen while rendering
// This might also queue an operation to set lapse to something else
// Actually do all that work in order
Ini semua tentang urutan apa yang ditambahkan ke antrian.
Salah satu pendekatan adalah mengirimkan reset hanya sekali timer benar-benar berhenti, yaitu pada efeknya. Jika Anda ingin selalu menampilkan nol dalam bingkai yang sama, maka Anda selalu dapat menampilkan nol saat menjalankan salah.
Namun, seperti hampir selalu, ini lebih baik dimodelkan sebagai peredam. Peredam dapat dengan mudah menolak pembaruan untuk selang ketika dianggap dalam keadaan berhenti dan juga melakukan logika untuk benar-benar mengatur ulang.
Aku mengerti sekarang. Terima kasih.
https://mobile.twitter.com/dan_abramov/status/1104069236132057089
https://codesandbox.io/s/23m1659rlp
Komentar yang paling membantu
Salah satu pendekatan adalah mengirimkan reset hanya sekali timer benar-benar berhenti, yaitu pada efeknya. Jika Anda ingin selalu menampilkan nol dalam bingkai yang sama, maka Anda selalu dapat menampilkan nol saat menjalankan salah.
Namun, seperti hampir selalu, ini lebih baik dimodelkan sebagai peredam. Peredam dapat dengan mudah menolak pembaruan untuk selang ketika dianggap dalam keadaan berhenti dan juga melakukan logika untuk benar-benar mengatur ulang.