Baudoin Michael
Baudoin Michael

Reputation: 101

Infinite loop with useEffect in React

This is my component:

import React, { useState, useEffect } from "react";

export default function App() {
  const [countriesArray, setCountriesArray] = useState([]);

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

  const getCountriesArray = async () => {
    try {
      let response = await fetch(
        "https://coronavirus-19-api.herokuapp.com/countries"
      );
      if (response.status === 200) {
        const newCountriesArray = [...countriesArray];
        const data = await response.json();
        await data.forEach(item => newCountriesArray.push(item.country));
        setCountriesArray(newCountriesArray);
      } else {
        setErrorStatus(true);
        console.error("Error status");
      }
    } catch (err) {
      console.error(err);
    }
  };

  const optionItems = countriesArray.map((item) =>
        <option key={item}>{item}</option>
    )

  return (
    <div className="App">
      <select>{optionItems}</select>
    </div>
  );
}

In the select I get the names of the countries when mounting the component but in the console I have a loop error message:

Warning: Encountered two children with the same key, `Total:`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
    in select (at App.js:36)
    in div (at App.js:35)
    in App
    in StrictMode (at src/index.js:8)

However I use the empty array as a second parameter of the useEffect to execute it only when mounting the component

Upvotes: 0

Views: 410

Answers (2)

Nicholas Tower
Nicholas Tower

Reputation: 84982

That error has nothing to do with the the effect's dependency array. The problem is that item.country is not unique in that data. The json includes 7 entries where country === "Total:".

A couple possibilities:

1) Filter out the duplicates. This is best if you don't care about those "Total:" entries.

if (response.status === 200) {
  const newCountriesArray = [...countriesArray];
  const data = await response.json();
  data.forEach(item => {
    if (item.country !== "Total:") {
      newCountriesArray.push(item.country)
    }
  });
  setCountriesArray(newCountriesArray);
}

2) Use a different key. Since this data does not have a unique key, you may need to use the index of the array. Be aware that this will not be a good option if you plan to sort this list.

const optionItems = countriesArray.map((item, index) =>
  <option key={index}>{item}</option>
)

Upvotes: 1

Carlos Saiz Orteu
Carlos Saiz Orteu

Reputation: 1805

You can use the key provided by the map itself this way:

const optionItems = countriesArray.map((item, key) =>
    <option key={key}>{item}</option>
)

That should solve your problem.

By the way, this is not an infinite loop problem, it is a duplicate key in you map function.

Upvotes: 1

Related Questions