書き換えられたタイトルは次のとおりです。promise.then() の各 setState がレンダリングされる前に React Promise.all().then() を呼び出す
P粉162773626
2023-08-20 11:02:53
<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> が「Complete」状態をトリガーし、進行状況バーが削除された後も、Promise 自体はまだ <code>then()</code> を呼び出しています。これにより、解決されたすべての Promise が表示される前に進行状況バーが非表示になります。 </p>
<p>この問題に正しく対処するにはどうすればよいですか? </p>
<p>デモでは、<code>setTimeout()</code> を使用して、高価なロジックをシミュレートします。 </p>
<p>問題は、<code>Promise.all.then: 20</code> が <code>Render 20</code> の後にある必要があることです。 </p>
<pre class="brush:none;toolbar:false;">レンダリング 0
...
レンダリング 12
Promise.all.then: 20 # 各レンダリング後にこれを記録する必要があります
レンダリング 13
...
レンダリング 19
レンダリング 20
</pre>
<p>デモで問題を示すために、進行状況バーが完全に埋まる前に削除されました (赤色に変わりました)。</p>
<p><br /></p>
<pre class="brush:js;toolbar:false;">const { useState } = React;
const 例 = () => {
const [done, setDone] = useState(false);
const [データ, setData] = useState({});
const demoData = Array.from(Array(20).keys());
const demoResolver = (x) =>新しい Promise(res => setTimeout(() => res(x), Math.random() * 1250))
constloadData = () => {
const Promise = DemonData.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)
setDone(true);
})
}
console.log('Render', Object.keys(data).length);
const progressBarIsShownDebugColor = (完了)
? 「危険です」
: '情報です';
戻る (
<セクションクラス名='セクション'>
<h1 className='title is-3'>{'例'}</h1>
<進捗状況
max={demoData.length}
値={オブジェクト.キー(データ).長さ}
className={'progress my-3 ' progressBarIsShownDebugColor}
/>
<button onClick={loadData}>開始</button>
</セクション>
)
}
ReactDOM.render(<Example />, document.getElementById("react"));</pre>
<pre class="brush:css;toolbar:false;">.as-console-wrapper { max-height: 50px !重要; }</pre>
<pre class="brush:html;toolbar:false;"><script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min .js">
<script 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>
上記のコードに示されている問題は、データを取得した後、状態を設定するまでにさらに 500 ミリ秒の非同期遅延が発生することです。実際のコードでは、
.all
の後にsetData
を呼び出す追加の処理 (おそらく同期) があるように見えます。ベスト プラクティスは、
リーリーdone
を個別の状態ではなく計算プロパティとして持つことです。その時点では、競合を設定するために状態に依存する必要がなく、Object .keys( data).length
は十分に安価であるため、パフォーマンスに悪影響を与えることはありません (また、他の領域でも使用できます。問題になった場合は、変数にキャッシュできます)。