Home > OS >  ReactJS How to Run useEffect After Props Value Changes But Only Run It Once
ReactJS How to Run useEffect After Props Value Changes But Only Run It Once

Time:09-02

I am using the Google Places API to run a nearby search followed by a detail search to get the hour data for each of the places returned. When I conduct the nearby search I collect all of the placeIds and pass them down as a prop to the component where I run my detail search. I use the useEffect ReactJs hook to make a fetch call to my nodeJs and Express backend and I send the place Ids over in the body of that fetch call. I loop over all of the IDs and add an API call for each ID to a promise array in the backend and then I send the results back over to the frontend.

This works perfectly for what I need. The problem is I am calling this useEffect on the placeId prop value change by passing [props.placeIds] as the callback to the useEffect hook. Having a callback in the useEffect hook makes the API call run continuously while the user has the app running. This, coupled with the fact that there are 15 Ids in each call, makes the app run thousands of API calls per minute which I imagine would be quite costly in production.

I know that if I pass a blank array to the useEffect hook it will only run once. The problem is when I do this the useEffect API call runs before it gets any of the placeIds which crashes the app.

How can I make it so the useEffect API call waits for props.placeIds to change and then runs only one time?

Here is my JSX fetch call to my backend:

useEffect(() => {
    fetch('/api/place-details-search', {
        method: 'POST',
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            placesId: props.placeIds,
            dayNumVal: props.dayNumVal,
            numOfPlaces: props.placeIds.length,
        })
    }).then(response => response.json()).then(data => {
        setPlaceHours(data.placeHourText)
    })
}, [props.placeIds])

and here is my nodeJS and express code that loops over the data and sends it back to the front end:

app.post('/api/place-details-search', (req, res) => {
    const dayNumVal = req.body.dayNumVal
    const numOfPlaces = req.body.numOfPlaces
    const placeIds = req.body.placeId
    let placeHourText = [];
    let placeHourName = [];
    let promises = [];
    let parsedDayNumVal
    if (dayNumVal == 0) {
        parsedDayNumVal = 6
    } else {
        parsedDayNumVal = dayNumVal - 1
    }
    for (var i = 0; i < numOfPlaces; i  ) {
        promises.push(
            axios.get('https://maps.googleapis.com/maps/api/place/details/json?place_id='   placeIds[i]   '&fields=name,opening_hours&key='   process.env.REACT_APP_GOOGLE_MAPS_API_KEY).then(response => {
                if (response.data.result !== undefined) {
                   placeHourText.push(response.data.result.opening_hours.weekday_text[parsedDayNumVal]);
                }
                if (response.data.result !== undefined) {
                    placeHourName.push(response.data.result.name)
                }
            })
        )
    }

    Promise.all(promises).then(() => {
        const placeHourTextTwo = []
        for (var i = 0; i < placeHourText.length; i  ) {
            let parsedPlaceHourText = placeHourText[i]
            parsedPlaceHourText = parsedPlaceHourText.split("y").pop()
            placeHourTextTwo.push([placeHourName[i], parsedPlaceHourText])
        }
        res.send({ placeHourText: placeHourTextTwo })
    });

});

CodePudding user response:

Having [props.placeIds] as a dependency to the useEffect() means: Hey Effect, please don't run unless props.placeIds changes, so if your setPlaceHours(data.placeHourText) function updates props.placeIds then it is diffently an infinite loop case. a possible solution would be to pass another prop that is derived from placeIds but it doesn't update in each API call

  • Related