Le titre réécrit est : Appel de React Promise.all().then() avant que chaque setState de promise.then() ne soit rendu
P粉162773626
2023-08-20 11:02:53
<p>J'essaie de passer 200 appels vers une ressource distante à afficher dans mon tableau, tout en affichant une barre de progression pour indiquer le nombre d'appels restants. </p>
<p>Utilisez cet exemple pour montrer comment utiliser <code>Fetch()</code> et <code>Promise.all()</code> /code> pour mettre à jour les nouvelles données. </p>
<p>Mon problème concerne le <code>.then()</code> de chaque promesse, qui calcule une certaine logique puis appelle <code>setState()</code> </p>
<p>Ma barre de progression utilise <code>Object.keys(data).length</code> </p>
<p>Après que <code>Promise.all()</code> déclenche l'état "Complet", supprimant la barre de progression, les promesses elles-mêmes appellent toujours leur <code>then()</code> , ce qui entraîne le masquage de la barre de progression avant que toutes les promesses résolues ne soient affichées. </p>
<p>Comment résoudre correctement ce problème ? </p>
<heure />
<p>Démo, utilisez <code>setTimeout()</code> pour simuler une logique coûteuse. </p>
<p>Le problème est que <code>Promise.all.then: 20</code> devrait être après <code>Render 20</code>. </p>
<pre class="brush:none;toolbar:false;">Rendu 0
...
Rendu 12
Promise.all.then : 20 # J'ai besoin que cela soit enregistré après chaque rendu
Rendu 13
...
Rendu 19
Rendu 20
≪/pré>
<p>Pour que la démo montre le problème, la barre de progression a été supprimée (devenue rouge) avant d'être complètement remplie.</p>
<p><br /></p>
<pre class="brush:js;toolbar:false;">const { useState } = React;
const Exemple = () => {
const [done, setDone] = useState(false);
const [données, setData] = useState({});
const demoData = Array.from(Array(20).keys());
const demoResolver = (x) => new Promise(res => setTimeout(() => res(x), Math.random() * 1250))
const loadData = () => {
const promises = demoData.map(c => demoResolver(c));
promesses.forEach(promesse => {
promesse
.then(r => {
setTimeout(() => {
setData(p => ({ ...p, [r]: r }));
}, 500);
})
});
Promesse.tout(promesses)
.then(r => {
console.log('Promise.all.then:', r.length)
setDone(vrai);
})
}
console.log('Render', Object.keys(data).length);
const progressBarIsShownDebugColor = (fait)
? 'est-danger'
: 'est-info';
retour (
<section className='section'>
<h1 className='title is-3'>{'Exemple'}</h1>
<progrès
max={demoData.length}
value={Object.keys(data).length}
className={'progress my-3' + progressBarIsShownDebugColor}
/>
<button onClick={loadData}>Démarrer</button>
</section>
)
}
ReactDOM.render(<Exemple />, document.getElementById("react"));</pre>
<pre class="brush:css;toolbar:false;">.as-console-wrapper { hauteur maximale : 50px !important; }</pré>
<pre class="brush:html;toolbar:false;"><script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min .js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<div id="react"></div></pre>
<p><br /></p>
Le problème montré dans le code ci-dessus est qu'après avoir obtenu les données, il y a un délai asynchrone supplémentaire de 500 ms avant de définir l'état. Dans le code réel, il semble qu'il y ait un traitement supplémentaire (probablement synchrone) qui provoque l'appel de
setData
在.all
.La meilleure chose à faire est de rendre
done
作为一个计算属性而不是一个单独的状态,因为在那个点上,您不需要依赖于状态设置竞争,并且Object.keys(data).length
suffisamment bon marché pour ne pas dégrader les performances (et vous l'utilisez dans d'autres domaines, si cela devient un problème, vous pouvez le mettre en cache dans une variable).