The rewritten title is: Calling React Promise.all().then() before each setState from promise.then() is rendered
P粉162773626
P粉162773626 2023-08-20 11:02:53
0
1
324

I'm trying to make 200 calls to a remote resource to display in my table, while displaying a progress bar to show the number of calls remaining.

Use this example to demonstrate how to use Fetch() and Promise.all() to call setState() to update new data.

My problem is with each promise's .then(), which calculates some logic and then calls setState() to update the data.

My progress bar uses Object.keys(data).length to show progress.

After Promise.all() triggers the "Complete" state, removing the progress bar, the promises themselves are still calling their then() , which causes the progress bar to be hidden before all resolved promises are displayed.

How to deal with this problem correctly?


Demo, use setTimeout() to simulate expensive logic.

The problem is that Promise.all.then: 20 should be after Render 20.

Render 0 ... Render 12 Promise.all.then: 20 # I need this to be recorded after each Render Render 13 ... Render 19 Render 20 

To make the demo show the problem, the progress bar was removed (turned red) before it was completely filled.


const { useState } = React; const Example = () => { const [done, setDone] = useState(false); const [data, 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)); promises.forEach(promise => { promise .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 = (done) ? 'is-danger' : 'is-info'; return ( 

{'Example'}

) } ReactDOM.render(, document.getElementById("react"));
.as-console-wrapper { max-height: 50px !important; }
   


P粉162773626
P粉162773626

reply all (1)
P粉426780515

The problem shown in the code above is that after getting the data, there is an additional 500ms async delay before setting the state. In the actual code, it sounds like there is additional processing (probably synchronous) that causessetDatato be called after.all.

The best practice is to havedoneas a computed property rather than a separate state, because at that point you don't need to rely on the state to set up contention, andObject.keys( data).lengthis cheap enough that it doesn't hurt performance (and you use it in other areas, you can cache it into a variable if it becomes a problem).

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 ( 

{'Example'}

) } ReactDOM.render(, document.getElementById("react"));
.as-console-wrapper { max-height: 50px !important; }
   
    Latest Downloads
    More>
    Web Effects
    Website Source Code
    Website Materials
    Front End Template
    About us Disclaimer Sitemap
    php.cn:Public welfare online PHP training,Help PHP learners grow quickly!