在使用 fetch API 获取数据和 setState 时,React useEffect 循环多次

React useEffect looping many times when fetching data and setState using fetch API

为什么会多次触发 fetchDataconsole.log 似乎几乎无限循环?我如何将其加载到 运行 一次并在稍后调用 fetchData() 时仅触发一次?我在这里做错了什么或遗漏了什么?

const [data, setData] = useState(null);

  let fetchData = React.useCallback(async () => {
    const result = await fetch(`api/data/get`);
    const body = await result.json();
    setData(body);
    console.log(data)
  },[data])

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

更新(附加问题):如何等待 datareturn() 之前填充,下面这个现在给出错误,因为它一开始是空的?:data.map is not a function

return (
    <select>
        {data.map((value, index) => {
            return <option key={index}>{value}</option>
        })}
    </select>
)

useCallback 挂钩中,您将 data 作为依赖项传递,同时通过调用 setData 更改回调内的数据值,这意味着每次数据值更改 fetchData 将重新初始化。

useEffect 钩子中 fetchData 是一个依赖项,这意味着每次 fetchData 更改都会触发 useEffect。这就是为什么你得到一个无限循环。

因为要在组件挂载的时候取一次数据,我觉得这里useCallback是不需要的。不需要记忆功能 fetchData 不必要的。

解决方案

const [data, setData] = useState(null);

useEffect(() => {
  const fetchData = async () => {
    try {
     const result = await fetch(`api/data/get`);
     const body = await result.json();
     setData(body);
    } catch(err) {
      // error handling code
    } 
  }

  // call the async fetchData function
  fetchData()

}, [])

如果您想在 data 的值发生变化时记录它,您可以使用另一个 useEffect 来代替。例如,

useEffect(() => {
  console.log(data)
}, [data])

P.S。 - 也不要让 promise 未处理,请使用 try...catch 块来处理错误。我已经更新了解决方案以包含 try..catch 块。

编辑 - 附加问题的解决方案 有两种可能的解决方案,

因为您希望 data 的值在 API 调用之后是一个数组,所以您可以将数据的值初始化为一个空数组,例如

const [data, setData] = useState([]);

但是如果出于某种原因你必须将 data 的值初始化为 null。以下是如何呈现从 API 调用返回的信息。

// using short-circuit evaluation
return (
    <select>
        {data && data.length && data.map((value) => {
            return <option key={`select-option-${value}`}>{value}</option>
        })}
    </select>
)


// using a ternary
return (
   <div>
    { data && data.length
       ? (<select>
            {
              data.map(value => <option key={`select-option-${value}`}>{value}</option>)
            }
           </select>
         )
       : <div>Data is still loading...</div>
    }
   </div>
)

不要将 indexes 用作 key,请使用唯一的密钥。