<p>我正在嘗試呼叫200個遠端資源以在我的表格中顯示,同時顯示一個進度條來顯示剩餘的呼叫次數。 </p>
<p>使用這個範例來示範如何使用<code>Fetch()</code>和<code>Promise.all()</code>來呼叫<code>setState()</ code>來更新新資料。 </p>
<p>我的問題在於每個promise的<code>.then()</code>,它計算一些邏輯,然後呼叫<code>setState()</code>來更新資料。 </p>
<p>我的進度列使用<code>Object.keys(data).length</code>來顯示進度。 </p>
<p>在<code>Promise.all()</code>觸發「完成」狀態後,移除進度條,promise本身仍在呼叫它們的<code>then()</code> ,這導致進度條在顯示所有已解決的promise之前就被隱藏了。 </p>
<p>如何正確處理這個問題? </p>
<hr />
<p>演示,使用<code>setTimeout()</code>來模擬昂貴的邏輯。 </p>
<p>問題在於<code>Promise.all.then: 20</code>應該在<code>Render 20</code>之後。 </p>
<pre class="brush:none;toolbar:false;">Render 0
…
Render 12
Promise.all.then: 20 # 我需要這個在每個Render之後記錄
Render 13
…
Render 19
Render 20
</pre>
<p>為了讓演示顯示出問題,進度條在完全填滿之前就被移除(變成紅色)。</p>
<p><br />></p>
const { useState } = React;
const 範例 = () => {
const [完成,setDone] = useState(false);
const [數據,setData] = useState({});
const demoData = Array.from(Array(20).keys());
const demoResolver = (x) =>;新的 Promise(res => setTimeout(() => res(x), Math.random() * 1250))
const loadData = () =>; {
const Promise = demoData.map(c => demoResolver(c));
Promise.forEach(promise => {
承諾
.then(r => {
setTimeout(() => {
setData(p => ({ ...p, [r]: r }));
}, 500);
})
});
Promise.all(承諾)
.then(r => {
console.log('Promise.all.then: ', r.length)
設定完成(真);
})
}
console.log('渲染', Object.keys(data).length);
const ProgressBarIsShownDebugColor =(完成)
? '是危險'
: '是信息';
返回 (
<節類別名稱='節'>
{'範例'}
;
<進展
最大值={demoData.length}
value={Object.keys(data).length}
className={'progress my-3'progressBarIsShownDebugColor}
>>
</節>
)
}
ReactDOM.render(<範例/>, document.getElementById("react"));
;
.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.production.min .js"></腳本>
<腳本 src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<連結 rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<div id="react"></div></pre>
<p><br />></p>
上面程式碼中顯示的問題是,在取得資料後,在設定狀態之前有額外的500ms非同步延遲。在實際程式碼中,聽起來有額外的處理(可能是同步的)導致
setData
在.all
之後被呼叫。最好的做法是將
done
作為一個計算屬性而不是一個單獨的狀態,因為在那個點上,您不需要依賴狀態設定競爭,並且Object.keys( data).length
足夠便宜,不會降低效能(而且您在其他區域使用它,如果它成為一個問題,您可以將其緩存到一個變數中)。