Le titre réécrit est : Appel de React Promise.all().then() avant que chaque setState de promise.then() ne soit rendu
P粉162773626
P粉162773626 2023-08-20 11:02:53
0
1
401
<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>
P粉162773626
P粉162773626

répondre à tous(1)
P粉426780515

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).

const [data, setData] = useState({});
const done = Object.keys(data).length === 20; // 在实际代码中为200

const { useState } = React;

const Example = () => {

    const [data, setData] = useState({});
    const done = Object.keys(data).length === 20; // 在实际代码中为200
      
    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));
          
        promises.forEach(promise => {
            promise
                .then(r => {
                    setTimeout(() => {
                        setData(p => ({ ...p, [r]: r }));
                    }, 500);
                })
        });
    }
    
    console.log('Render', Object.keys(data).length);
  
    const progressBarIsShownDebugColor = (done)
      ? 'is-danger'
      : 'is-info';
    
    return (
        <section className='section'>
            <h1 className='title is-3'>{'Example'}</h1>
            <progress 
                max={demoData.length}
                value={Object.keys(data).length} 
                className={'progress my-3 ' + progressBarIsShownDebugColor}
            />
            <button onClick={loadData}>Start</button>
        </section>
    )
}
ReactDOM.render(<Example />, document.getElementById("react"));
.as-console-wrapper { max-height: 50px !important; }
<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>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal