简体   繁体   中英

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

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.

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>
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM