React: La définition de l'état dans les événements discrets doit-elle entraîner l'exécution du nettoyage ?

Créé le 5 mars 2019  ·  3Commentaires  ·  Source: facebook/react

Ce bug est assez déroutant :

https://twitter.com/kentcdodds/status/1102659818660102145

Je pense que cela arrive parce que fn programmé par setInterval(fn, 0) saute devant le nettoyage de l'effet [running] causé par setRunning(false) . Ainsi, l'intervalle se déclenche toujours, écrasant setLapse(0) qui s'est produit pendant l'événement avec son setLapse(someValue) .

Cela me rappelle le problème décrit dans https://github.com/facebook/react/issues/14750#issuecomment -460409609, ou au moins une partie de celui-ci :

En fait, ce problème existe même pour les frappes régulières de React (et d'autres événements "discrets"). La solution à cela serait d'éliminer les effets passifs avant d'avoir un événement discret.

Mais ici, il semble que cela ne soit pas suffisant car l'effet bascule à la suite du clic, pas avant. Donc, setState intérieur d'un événement discret devrait-il également éliminer l'effet passif ? On dirait que non. (Cela irait à l'encontre du but de les retarder.)

Cela fonctionne donc comme prévu, et le correctif n'est que de useLayoutEffect lorsque le timing compte ? Ou la solution rAF ?

Hooks Question

Commentaire le plus utile

Une approche consiste à n'envoyer la réinitialisation qu'une fois que la minuterie s'arrête réellement, c'est-à-dire dans l'effet. Si vous voulez qu'il affiche toujours zéro dans le même cadre, vous pouvez toujours afficher zéro lorsque l'exécution est fausse.

Cependant, comme presque toujours, cela est mieux modélisé comme un réducteur. Le réducteur peut facilement refuser l'expiration des mises à jour lorsqu'il pense qu'il est à l'arrêt et également exécuter la logique pour le réinitialiser.

Tous les 3 commentaires

Le problème est que la minuterie elle-même n'est pas un événement discret. Les événements discrets ne sont garantis que par rapport à d'autres événements discrets. Je ne pense pas que ce soit ce que vous voulez ici.

L'arrêt du temporisateur est une opération asynchrone puisque tous les états définis sont asynchrones. Donc quelque chose d'autre peut entrer avant qu'il ne soit rincé.

C'est un cas où useLayoutEffect ne résout pas complètement le problème. Certainement pas en mode concurrent, mais c'est aussi sommaire en mode synchro. S'il ne s'agissait pas d'une minuterie mais d'un événement de focus, cela peut se déclencher dans un lot avant qu'il ne soit vidé, ce qui poserait le même problème.

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

Tout dépend de l'ordre dans lequel les choses sont ajoutées à la file d'attente.

Une approche consiste à n'envoyer la réinitialisation qu'une fois que la minuterie s'arrête réellement, c'est-à-dire dans l'effet. Si vous voulez qu'il affiche toujours zéro dans le même cadre, vous pouvez toujours afficher zéro lorsque l'exécution est fausse.

Cependant, comme presque toujours, cela est mieux modélisé comme un réducteur. Le réducteur peut facilement refuser l'expiration des mises à jour lorsqu'il pense qu'il est à l'arrêt et également exécuter la logique pour le réinitialiser.

Cette page vous a été utile?
0 / 5 - 0 notes