Search code examples
node.jsreactjsaxiosuse-effectuse-state

Infinite loop when pushing into array using UseState()?


I would like to store the data of a response into an array for reuse. I am using Axios for this. The issue I receive is that when I push into the array, it loops getBoroughAndId() and keeps pushing into the array. I can tell because I get a console.log() response where it keeps telling me I am making too many requests. Any advice? Thanks.

Edit: After taking another gander, I think the issue is that the id is always changing when running getBoroughAndId. I'm not sure how to stop this.

import React, { useEffect, useState } from 'react';
import { airtableApi } from '../services/api/airtable';
import { BoroughDay, BoroughGroup } from '../types/api';

const IndexPage = () => {
  const [boroughs, setBoroughs] = useState<BoroughDay[]>([]);
  const [boroughGroups, setBoroughGroups] = useState<BoroughGroup[]>([]);

  const getBoroughsAndDays = () => {
    airtableApi
      .getBoroughsAndDays()
      .then((response) => {
        setBoroughs(response);
      })
      .catch(() => { });
  };

  const getBoroughAndId = (id: string) => {
    airtableApi
      .getBoroughAndId(id)
      .then((response) => {
        console.log(response);
        setBoroughGroups(arr => [...arr, response])
        return response;
      })
      .catch(() => { });
  }

  useEffect(() => {
    getBoroughsAndDays()
  }, [boroughGroups])

  return (
    <>
      {boroughs.map((data) => {
        getBoroughAndId(data.id);
      })}
    </>
  )
}

export default IndexPage

Here is my corrected code. It works a lot better now, with less nonsense and everything being done in the first function.

import React, { useEffect, useState } from 'react';
import { airtableApi } from '../services/api/airtable';
import { BoroughDay, BoroughGroup } from '../types/api';

const IndexPage = () => {

  const [boroughs, setBoroughs] = useState<BoroughDay[]>([]);
  const [boroughGroups, setBoroughGroups] = useState<BoroughGroup[]>([]);

  const getBoroughsDays = () => {
    airtableApi
      .getBoroughsAndDays()
      .then((response) => {
        setBoroughs(response.records);
        response.records.map((data) => {
          setBoroughGroups(arr => [...arr, {id: data.id, "Borough": data.fields["Borough"]}])
        })
      })
      .catch(() => { })
  }

  useEffect(() => {
    getBoroughsDays();
  }, [])

  return (
    <>
      {boroughGroups.map(data => <div>{data.id} {data.Borough}</div>)}
    </>
  )
}

export default IndexPage

Solution

  • In order to tell you the mistake you are committing, I will tell you the whole flow of your program.

    1. First of all when component mounts, useEffect will be called, which will call the getBoroughsAndDays function.

    Note: boroughGroups in dependency array in useEffect is causing an infinite loop

    1. This function (getBoroughsAndDays()) will update the value of boroughs(using setBoroughs)

    2. Now since the state updated the function will re render, hence output will be shown on the screen

    3. Now observe, here you are calling "getBoroughAndId(data.id)" function (inside map function), which is updating the value of boroughGroups(using setBoroughGroups)

    4. Now since the value of boroughGroups have changed, the useEffect method will be called, which will again trigger the "getBoroughsandDays()" function, repeating the whole process again, so that is the reason, it is creating infinite loop.

    Note: When any value inside dependency array changes useEffect will be called.

    Solution: I don't know what functionality you want to achieve but remove "boroughGroups" dependency from useEffect (In this way it will behave like componentDidMount).