Home > database >  React : Why my setState returns an array, but the state is a promise when the component rerender?
React : Why my setState returns an array, but the state is a promise when the component rerender?

Time:09-05

The code below try to check if an url is reachable or not. The urls to check are stored in a state called trackedUrls I update this state with an async function checkAll. The object just before being updated seems fine, but when the component rerender, it contains a promise !

  • Why ?
  • What I should change to my code ?
import React from "react"

export default function App() {

    const [trackedUrls, setTrackedUrls] = React.useState([])

    // 1st call, empty array, it's ok
    // 2nd call, useEffect populate trackedUrls with the correct value
    // 3rd call, when checkAll is called, it contains a Promise :/
    console.log("trackedUrls  :", trackedUrls)
    const wrappedUrls = trackedUrls.map(urlObject => {
        return (
            <div key={urlObject.id}>
                <a href={urlObject.url} target="_blank" rel="noopener noreferrer">{urlObject.label}</a>
            </div>
        )
    })

    // check if the url is reachable
    // this works well if cors-anywhere is enable, click the button on the page
    async function checkUrl(url) {
        const corsUrl = "https://cors-anywhere.herokuapp.com/"   url
        const result = await fetch(corsUrl)
            .then(response => response.ok)
        console.log(result)
        return result
    }

    // Checks if every url in trackedUrls is reachable
    // I check simultaneously the urls with Promise.all
    async function checkAll() {
        setTrackedUrls(async oldTrackedUrls => {
            const newTrackedUrls = await Promise.all(oldTrackedUrls.map(async urlObject => {
                let isReachable = await checkUrl(urlObject.url)
                const newUrlObject = {
                    ...urlObject,
                    isReachable: isReachable
                }
                return newUrlObject
            }))

            // checkAll works quite well ! the object returned seems fine
            // (2) [{…}, {…}]
            // { id: '1', label: 'google', url: 'https://www.google.Fr', isReachable: true }
            // { id: '2', label: 'whatever', url: 'https://qmsjfqsmjfq.com', isReachable: false }
            console.log(newTrackedUrls)
            return newTrackedUrls
        })
    }

    React.useEffect(() => {
        setTrackedUrls([
            { id: "1", label: "google", url: "https://www.google.Fr" },
            { id: "2", label: "whatever", url: "https://qmsjfqsmjfq.com" }
        ])
    }, [])

    return (
        <div>
            <button onClick={checkAll}>Check all !</button>
            <div>
                {wrappedUrls}
            </div>
        </div>
    );
}

CodePudding user response:

Konrad helped me to grasp the problem. This works and it's less cumbersome. If anyone has a solution with passing a function to setTrackedUrls, I'm interested just for educational purpose.

    async function checkAll() {
        const newTrackedUrls = await Promise.all(trackedUrls.map(async urlObject => {
            let isReachable = await checkUrl(urlObject.url)
            const newUrlObject = {
                ...urlObject,
                isReachable: isReachable
            }
            return newUrlObject
        }))
        setTrackedUrls(newTrackedUrls)
    }

CodePudding user response:

  1. Don't use async for something like this.
  2. Don't use React.use***. Hooks very good work and without it.
  3. Add Typescript to you project, it will be punch you for sh*t like this.
  4. And Last - answer on you quetion. You can put in setState only data. Good luck
  • Related