react hooks (useEffect) 无限循环说明
react hooks (useEffect) infinite loop clarification
这个问题与我的 上一个问题有关。我写了两个自定义挂钩。第一个是 useFetchAccounts
,它的工作是获取有关用户帐户的一些数据,如下所示:
// a fake fetch for the sake of the example
function my_fetch(f, delay) {
setTimeout(f, delay)
}
function useFetchAccounts() {
const [data, setData] = useState({accounts: [], loaded: false})
useEffect(() => {
my_fetch(() => {setData({accounts: [{id:1},{id:2}], loaded: true})}, 3000)
}, [])
return data
}
这个钩子没什么特别的,只是获取帐户的 ID。
现在我有另一个钩子,useFetchBalancesWithIDs(account_ids, accounts_loaded)
,它用于获取上一步中的 ID 并在帐户已加载的情况下获取这些帐户的余额。它看起来像这样:
function useFetchBalanceWithIDs(account_ids, accounts_loaded) {
const [balances, setBalances] = useState(null)
useEffect(() => {
if (!accounts_loaded) return; // only fetch if accounts are loaded
my_fetch(() => {
setBalances(account_ids.map(id => 42+id))
}, 3000)
}, [account_ids, accounts_loaded])
return balances
}
如您所见,如果 accounts_loaded
为假,则不会执行提取。我一起使用它们如下:
function App() {
const account_data = useFetchAccounts()
const accounts = account_data.accounts
const account_ids = accounts.map(account => account.id) // extract ids
const balance = useFetchBalanceWithIDs(account_ids, account_data.loaded)
console.log(balance)
return null
}
不幸的是,这会导致无限循环。起作用的是将 useFetchBalancesWithIDs
更改为:
function useFetchBalanceWithAccounts(accounts, accounts_loaded) {
const [balances, setBalances] = useState(null)
useEffect(() => {
if (!accounts_loaded) return; // only fetch if accounts are loaded
my_fetch(() => {
setBalances(accounts.map(account => 42+account.id))
}, 3000)
}, [accounts, accounts_loaded])
return balances
}
在其中执行ids提取。我是这样使用它的:
function App() {
const account_data = useFetchAccounts()
const accounts = account_data.accounts
const balance = useFetchBalanceWithAccounts(accounts, account_data.loaded)
console.log(balance)
return null
}
运行良好。因此,问题似乎与从 App 组件中提取 ID 有关,这似乎一直在触发 useFetchBalanceWithIDs
,即使 account_ids
不会更改值。有人可以帮助解释这种行为吗?我可以使用 useFetchBalanceWithAccounts
但我想了解为什么 useFetchBalanceWithIDs
不起作用。谢谢!
问题是您正在使用地图函数来获取您的帐户 ID 数组。 React 使用 Object.is 来确定依赖数组中的值在每个渲染周期中是否发生了变化。 map 函数 returns 一个新数组,因此即使数组中的值相同,比较检查的计算结果为 false,效果为 运行.
useEffect
使用引用相等,这意味着 [1,2,3]
不等于 [1,2,3]
const data = [{id:1}];
data.map(d=>d.id)===data.map(d=>d.id);//is false
防止 id 中的数组更改引用的一种方法是在 App 中将 useState 与 useEffect 结合使用:
function App() {
//store account id's in app state
const [accountIds, setAccountIds] = useState([]);
const account_data = useFetchAccounts();
const accounts = account_data.accounts;
//only if accounts change re set the id's
useEffect(() => {
setAccountIds(accounts.map(a => a.id));
}, [accounts]);
const balance = useFetchBalanceWithAccounts(
accountIds,
account_data.loaded
);
console.log(balance);
return null;
}
这个问题与我的 useFetchAccounts
,它的工作是获取有关用户帐户的一些数据,如下所示:
// a fake fetch for the sake of the example
function my_fetch(f, delay) {
setTimeout(f, delay)
}
function useFetchAccounts() {
const [data, setData] = useState({accounts: [], loaded: false})
useEffect(() => {
my_fetch(() => {setData({accounts: [{id:1},{id:2}], loaded: true})}, 3000)
}, [])
return data
}
这个钩子没什么特别的,只是获取帐户的 ID。
现在我有另一个钩子,useFetchBalancesWithIDs(account_ids, accounts_loaded)
,它用于获取上一步中的 ID 并在帐户已加载的情况下获取这些帐户的余额。它看起来像这样:
function useFetchBalanceWithIDs(account_ids, accounts_loaded) {
const [balances, setBalances] = useState(null)
useEffect(() => {
if (!accounts_loaded) return; // only fetch if accounts are loaded
my_fetch(() => {
setBalances(account_ids.map(id => 42+id))
}, 3000)
}, [account_ids, accounts_loaded])
return balances
}
如您所见,如果 accounts_loaded
为假,则不会执行提取。我一起使用它们如下:
function App() {
const account_data = useFetchAccounts()
const accounts = account_data.accounts
const account_ids = accounts.map(account => account.id) // extract ids
const balance = useFetchBalanceWithIDs(account_ids, account_data.loaded)
console.log(balance)
return null
}
不幸的是,这会导致无限循环。起作用的是将 useFetchBalancesWithIDs
更改为:
function useFetchBalanceWithAccounts(accounts, accounts_loaded) {
const [balances, setBalances] = useState(null)
useEffect(() => {
if (!accounts_loaded) return; // only fetch if accounts are loaded
my_fetch(() => {
setBalances(accounts.map(account => 42+account.id))
}, 3000)
}, [accounts, accounts_loaded])
return balances
}
在其中执行ids提取。我是这样使用它的:
function App() {
const account_data = useFetchAccounts()
const accounts = account_data.accounts
const balance = useFetchBalanceWithAccounts(accounts, account_data.loaded)
console.log(balance)
return null
}
运行良好。因此,问题似乎与从 App 组件中提取 ID 有关,这似乎一直在触发 useFetchBalanceWithIDs
,即使 account_ids
不会更改值。有人可以帮助解释这种行为吗?我可以使用 useFetchBalanceWithAccounts
但我想了解为什么 useFetchBalanceWithIDs
不起作用。谢谢!
问题是您正在使用地图函数来获取您的帐户 ID 数组。 React 使用 Object.is 来确定依赖数组中的值在每个渲染周期中是否发生了变化。 map 函数 returns 一个新数组,因此即使数组中的值相同,比较检查的计算结果为 false,效果为 运行.
useEffect
使用引用相等,这意味着 [1,2,3]
不等于 [1,2,3]
const data = [{id:1}];
data.map(d=>d.id)===data.map(d=>d.id);//is false
防止 id 中的数组更改引用的一种方法是在 App 中将 useState 与 useEffect 结合使用:
function App() {
//store account id's in app state
const [accountIds, setAccountIds] = useState([]);
const account_data = useFetchAccounts();
const accounts = account_data.accounts;
//only if accounts change re set the id's
useEffect(() => {
setAccountIds(accounts.map(a => a.id));
}, [accounts]);
const balance = useFetchBalanceWithAccounts(
accountIds,
account_data.loaded
);
console.log(balance);
return null;
}