Tajuk yang ditulis semula ialah: Memanggil React Promise.all().then() sebelum setiap setState daripada promise.then() diberikan
P粉162773626
2023-08-20 11:02:53
<p>Saya cuba membuat 200 panggilan ke sumber jauh untuk dipaparkan dalam jadual saya, sambil memaparkan bar kemajuan untuk menunjukkan bilangan panggilan yang tinggal. </p>
<p>Gunakan contoh ini untuk menunjukkan cara menggunakan <code>Fetch()</code> dan <code>Promise.all()</code> untuk memanggil <code>setState()< / kod> untuk mengemas kini data baharu. </p>
<p>Masalah saya ialah dengan <kod>.then()</code> setiap janji, yang mengira beberapa logik dan kemudian memanggil <code>setState()</code> </p>
<p>Bar kemajuan saya menggunakan <kod>Object.keys(data).length</code> </p>
<p>Selepas <code>Promise.all()</code> mencetuskan keadaan "Lengkap", mengalih keluar bar kemajuan, janji itu sendiri masih memanggil <kod>then()</code> , yang menyebabkan bar kemajuan disembunyikan sebelum semua janji yang diselesaikan dipaparkan. </p>
<p>Bagaimana untuk menangani masalah ini dengan betul? </p>
<hr />
<p>Demo, gunakan <code>setTimeout()</code> </p>
<p>Masalahnya ialah <code>Promise.all.then: 20</code> hendaklah selepas <code>Render 20</code>. </p>
<pre class="brush:none;toolbar:false;">Render 0
...
Render 12
Promise.all.then: 20 # Saya memerlukan ini untuk dilog selepas setiap Render
Render 13
...
Render 19
Render 20
</pra>
<p>Untuk membuat demo menunjukkan masalah, bar kemajuan telah dialih keluar (bertukar merah) sebelum ia diisi sepenuhnya.</p>
<p><br /></p>
<pre class="brush:js;toolbar:false;">const { useState } = React;
const Contoh = () => {
const [selesai, setDone] = useState(false);
const [data, setData] = useState({});
const demoData = Array.from(Array(20).keys());
const demoResolver = (x) => Janji baharu(res => setTimeout(() => res(x), Math.random() * 1250))
const loadData = () => {
const promises = demoData.map(c => demoResolver(c));
promises.forEach(janji => {
janji
.then(r =>> {
setTimeout(() => {
setData(p => ({ ...p, [r]: r }));
}, 500);
})
});
Janji.semua(janji)
.then(r =>> {
console.log('Promise.all.then: ', r.length)
setDone(benar);
})
}
console.log('Render', Object.keys(data).length);
const progressBarIsShownDebugColor = (selesai)
? 'bahaya'
: 'is-info';
kembali (
<section className='section'>
<h1 className='title is-3'>{'Contoh'}</h1>
<kemajuan
max={demoData.length}
value={Object.keys(data).length}
className={'progress my-3 ' + progressBarIsShownDebugColor}
/>
<butang onClick={loadData}>Mula</button>
</section>
)
}
ReactDOM.render(<Contoh />, 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.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>
Masalah yang ditunjukkan dalam kod di atas ialah selepas mendapat data, terdapat kelewatan async 500ms tambahan sebelum menetapkan keadaan. Dalam kod sebenar, ia kelihatan seperti terdapat pemprosesan tambahan (mungkin segerak) yang menyebabkan
setData
在.all
dipanggil selepas.Perkara terbaik untuk dilakukan ialah membuat
done
作为一个计算属性而不是一个单独的状态,因为在那个点上,您不需要依赖于状态设置竞争,并且Object.keys(data).length
cukup murah supaya ia tidak merendahkan prestasi (dan anda menggunakannya di kawasan lain, jika ia menjadi masalah, anda boleh menyimpannya ke dalam pembolehubah).