Home > Net >  Too many re-renders. React limits the number of renders to prevent an infinite loop and post to API
Too many re-renders. React limits the number of renders to prevent an infinite loop and post to API

Time:11-03

I have an array that is made of multiple objects (9 to be exact). I'm looping through the array using .map() and then use axios.post() to post each single object with its corresponding name to a MySQL database.

However, when I run the code it gives me the error message: "Too many re-renders. React limits the number of renders to prevent an infinite loop" which is being caused by the "parsedData.map()" function.

How can I avoid that, so that I can get each objects data and send it to the API?

    const [parsedData, setParsedData] = useState([]);
    const [addInputData, SetAddInputData] = useState([]);

    const handleSubmit = (event) => {
        Papa.parse(event.target.files[0], {
            header: true,
            skipEmptyLines: true,
            complete: function (results) {
                setParsedData(results.data);
            },
        });
    };

    parsedData.map((person) => {
        SetAddInputData({
            status: person.status,
            first_name: person.first_name,
            last_name: person.last_name,
            position: person.position,
            email: person.email,
            phone: person.phone,
            image_url: person.image_url,
            linked_in: person.linked_in,
            business_name: person.business_name,
            postcode: person.postcode,
            icebreaker: person.icebreaker,
            paragraph_one: person.paragraph_one,
            paragraph_two: person.paragraph_two,
            paragraph_three: person.paragraph_three,
            call_to_action: person.call_to_action,
        });
        addNewLead({ addInputData }); // axios.post(`${base_url}/leads`, addInputData) in a different file
    });

CodePudding user response:

If this is inside of a functional component you are in fact causing an infinite loop. Each time the component function runs parsedData.map(...) runs and updates the addInputData state causing another run of the component function, and another call to parsedData.map(...)

The addInputData is derived state from parsedData, so you should probably do your update stuff in a useEffect(...,[parsedData]). Another option would be to use the primary state directly and not do the derived state at all. Although without useEffect you are going to call the axios api every time the component renders.

CodePudding user response:

In React, a component re-renders whenever one of these 2 cases occurs:

  1. Whenever there is a change in the component's local state.
  2. Whenever any of it's parent components themselves are re-rendered.

Now, whenever a component re-renders, it re-evaluates all logic within it as well.
Meaning that if you were to put a map function inside of the component like in your example, it will map every time the component re-renders.

And since you set the component's state within that map function, it creates a loop that looks like this:

  • The component mounts
  • The map function is being called
  • The map function sets the state
  • The component re-renders
  • The map function runs again

And so on, and so forth...
This creates what is called an "infinite loop", and it ultimately results in the error you are receiving, which is an error caught by React in-order to avoid the usual case with infinite loops, in-which the browser would freeze and eventually crash.

As for the solution, the way we usually handle state population with the value returned from async calls, is via the useEffect hook, which is a built-in React hook that activates a callback whenever there is a change in any of it's dependencies.

Also, you probably don't want to set the state within the map function.
It will reset the state with an object with every iteration instead of setting it as an array of those object.
You likely want to wrap the map function with the setState function.

Example:

function MyComp() {
   const [parsedData, setParsedData] = useState([]);
   const [addInputData, setAddInputData] = useState([]);

   useEffect(() => {
      setAddInputData(parsedData.map(person => ({
         status: person.status,
         first_name: person.first_name,
         last_name: person.last_name,
         position: person.position,
         email: person.email,
         phone: person.phone,
         image_url: person.image_url,
         linked_in: person.linked_in,
         business_name: person.business_name,
         postcode: person.postcode,
         icebreaker: person.icebreaker,
         paragraph_one: person.paragraph_one,
         paragraph_two: person.paragraph_two,
         paragraph_three: person.paragraph_three,
         call_to_action: person.call_to_action,
      })));
   }, []); // A useEffect with an empty dependency array should run only once when the component first mounts.

   useEffect(() => {
      addNewLead({ addInputData });
   }, [addInputData]); // A useEffect that is dependent upon a state, will run every time that state was set.
}
  • Related