0

I am having difficulty getting a mapped fetch function to run inside a useEffect.

I currently have a useEffect that runs a function. First it obtains data from a fetch request and then it must map a fetch function on that data to update it array by array.

useEffect:

useEffect(() => {
    fetchData('')
}, [])

first it fetches data from an API and then within the same function it performs some recalculation:

const fetcData = async (time) => {
    fetch(`http://...`, {
        method: 'POST',
        body:   JSON.stringify({ //converts body to json
            time: time
        }),
        headers: {
            "Content-type": "application/json; charset=UTF-8"
        }
    }).then(res => res.json())
        .then(res => { 

            setA(res.data.a);
            setB(res.data.b);
            const recalcData = recalculateData(res.data.c)
            setCopyOptions(recalcData)
            setDataLoaded(true);
        })
        .catch(err => {
            console.log(err)
            setDataErr(true)
        })
}

The recalculation step is done here:

const recalculateData = (data) => {
    let tempCopy = JSON.parse(JSON.stringify(data))
    copyOptions.map((item, ix) => (
        fetch(`http:...`, {
            method: 'POST',
            body:   JSON.stringify({ //converts body to json
                data: data[ix]
            }),
            headers: {
                "Content-type": "application/json; charset=UTF-8"
            }
        }).then(res => res.json())
            .then(res => {
                tempCopy[ix].a = res.a
                tempCopy[ix].b = res.b
                tempCopy[ix].c = res.c
            })
    ))
    return tempCopy
}

I am also using this recalculateData function elsewhere. It works fine when I trigger it from a button but it doesn't trigger inside useEffect. I'm not an expert on async functions so any pointers will be greatly appreciated.

Kafkaesque
  • 37
  • 3
  • 1
    When should the effect be triggered? Only initially? Because that's what you defined (empty brackets). If the effect should be triggered under any other circumstances you should add those dependencies in the dependency array of the effect. – André Frings Nov 16 '22 at 09:56
  • Firstly, try `Promise.all` for a list of fetch calls. Secondly, put state value in dependencies array of useEffect if it's used in your function – Ken Ha Nov 16 '22 at 10:06
  • Does this answer your question? [How to call an async function inside a UseEffect() in React?](https://stackoverflow.com/questions/56838392/how-to-call-an-async-function-inside-a-useeffect-in-react) – Michael Freidgeim Feb 21 '23 at 18:56

1 Answers1

0

In the recalculateData you are not awaiting the fetch requests. To return a properly updated response, make the recalculateData an async function and write code like this. And also await recalculateData

const recalculateData = async (data) => {
    let tempCopy = JSON.parse(JSON.stringify(data))
    const func = async (item, ix) => {
       const jsonRes = await fetch(`http:...`, {
            method: 'POST',
            body:   JSON.stringify({ //converts body to json
                data: data[ix]
            }),
            headers: {
                "Content-type": "application/json; charset=UTF-8"
            }
        })
        const res = await jsonRes()
        tempCopy[ix].a = res.a
        tempCopy[ix].b = res.b
        tempCopy[ix].c = res.c
    }
    for(let i=0;i<copyOptions.length;i++){
        await func(copyOptions[i], i);
    }
    return tempCopy
}

And I have also rewritten fetchData accordingly.

const fetchData = async (time) => {
    try{   
        const jsonRes = await fetch(`http://...`, {
        method: 'POST',
        body:   JSON.stringify({ //converts body to json
            time: time
        }),
        headers: {
            "Content-type": "application/json; charset=UTF-8"
        }
        })
        const res = await jsonRes()

        setA(res.data.a);
        setB(res.data.b);
        const recalcData = await recalculateData(res.data.c)
        setCopyOptions(recalcData)
        setDataLoaded(true);
     } catch (err) {
        console.log(err)
        setDataErr(true)
     }
}

I do not have any idea why you are making fetch requests using a map method. And I am also not sure if the fetch requests have to be made sequentially. If they need not be made sequentially use Promise.all() to await until the list of fetch promises succeeds.

Hope this solves your problem.