Heim  >  Fragen und Antworten  >  Hauptteil

Der umgeschriebene Titel lautet: Aufruf von React Promise.all().then(), bevor jeder setState von Promise.then() gerendert wird

<p>Ich versuche, 200 Anrufe an eine Remote-Ressource zu tätigen, um sie in meiner Tabelle anzuzeigen, und zeige gleichzeitig einen Fortschrittsbalken an, der die Anzahl der verbleibenden Anrufe anzeigt. </p> <p>Verwenden Sie dieses Beispiel, um zu demonstrieren, wie Sie <code>Fetch()</code> und <code>Promise.all()</code> verwenden. /code> um neue Daten zu aktualisieren. </p> <p>Mein Problem ist mit <code>.then()</code> jedes Versprechens, das eine gewisse Logik berechnet und dann <code>setState()</code> aufruft, um die Daten zu aktualisieren. </p> <p>Mein Fortschrittsbalken verwendet <code>Object.keys(data).length</code> </p> <p>Nachdem <code>Promise.all()</code> den Status „Abgeschlossen“ auslöst und der Fortschrittsbalken entfernt wird, rufen die Versprechen selbst weiterhin ihre Funktion <code>then()</code> auf. , wodurch der Fortschrittsbalken ausgeblendet wird, bevor alle gelösten Versprechen angezeigt werden. </p> <p>Wie geht man mit diesem Problem richtig um? </p> <hr /> <p>Demo, verwenden Sie <code>setTimeout()</code>, um teure Logik zu simulieren. </p> <p>Das Problem ist, dass <code>Promise.all.then: 20</code> nach <code>Render 20</code> stehen sollte. </p> <pre class="brush:none;toolbar:false;">Rendern 0 ... Rendern Sie 12 Promise.all.then: 20 # Ich muss dies nach jedem Rendern protokollieren Rendern 13 ... Rendern 19 Rendern Sie 20 </pre> <p>Damit die Demo das Problem zeigt, wurde der Fortschrittsbalken entfernt (rot), bevor er vollständig ausgefüllt war.</p> <p><br /></p> <pre class="brush:js;toolbar:false;">const { useState } = React; const Beispiel = () => { const [done, setDone] = useState(false); const [data, setData] = useState({}); const demoData = Array.from(Array(20).keys()); const demoResolver = (x) => neues Versprechen(res => setTimeout(() => res(x), Math.random() * 1250)) const loadData = () => { const Promises = demoData.map(c => demoResolver(c)); verspricht.forEach(promise => { versprechen .then(r => { setTimeout(() => { setData(p => ({ ...p, [r]: r })); }, 500); }) }); Promise.all(promises) .then(r => { console.log('Promise.all.then: ', r.length) setDone(true); }) } console.log('Render', Object.keys(data).length); const progressBarIsShownDebugColor = (erledigt) ? 'ist-Gefahr' : 'ist-info'; zurückkehren ( <section className='section'> <h1 className='title is-3'>{'Example'}</h1> <Fortschritt max={demoData.length} value={Object.keys(data).length} className={'progress my-3 ' + progressBarIsShownDebugColor} /> <button onClick={loadData}>Start</button> </Abschnitt> ) } ReactDOM.render(<Example />, document.getElementById("react"));</pre> <pre class="brush:css;toolbar:false;">.as-console-wrapper { max-height: 50px !important; }</pre> <pre class="brush:html;toolbar:false;"><script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.produktion.min .js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.produktion.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粉162773626P粉162773626427 Tage vor371

Antworte allen(1)Ich werde antworten

  • P粉426780515

    P粉4267805152023-08-21 00:02:16

    上面代码中显示的问题是,在获取数据后,在设置状态之前有额外的500ms异步延迟。在实际代码中,听起来有额外的处理(可能是同步的)导致setData.all之后被调用。

    最好的做法是将done作为一个计算属性而不是一个单独的状态,因为在那个点上,您不需要依赖于状态设置竞争,并且Object.keys(data).length足够便宜,不会降低性能(而且您在其他区域使用它,如果它成为一个问题,您可以将其缓存到一个变量中)。

    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>

    Antwort
    0
  • StornierenAntwort