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 a dependency to the useEffect hook. Having a dependency 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%2Copening_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 })
});
});
Having [props.placeIds]
as a dependency to the useEffect()
means:
props.placeIds
changes,So if your setPlaceHours(data.placeHourText)
function updates props.placeIds
then it is differently 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