在使用 fetch API 获取数据和 setState 时,React useEffect 循环多次
React useEffect looping many times when fetching data and setState using fetch API
为什么会多次触发 fetchData
? console.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]);
更新(附加问题):如何等待 data
在 return()
之前填充,下面这个现在给出错误,因为它一开始是空的?: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
,请使用唯一的密钥。
为什么会多次触发 fetchData
? console.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]);
更新(附加问题):如何等待 data
在 return()
之前填充,下面这个现在给出错误,因为它一开始是空的?: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
,请使用唯一的密钥。