react hooks(useEffect)无限循环说明

时间:2019-08-24 09:37:47

标签: reactjs react-hooks use-effect

这个问题与我的this先前的问题有关。我写了两个自定义钩子。第一个是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是否为false,它将不会执行提取。我在一起使用它们的方式如下:

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
}

在其中执行id提取。我正在这样使用它:

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不起作用。谢谢!

2 个答案:

答案 0 :(得分:1)

问题在于您正在使用map函数获取帐户ID的数组。 React使用Object.is确定在每个渲染周期中依赖数组中的值是否已更改。 map函数会返回一个新数组,因此即使数组中的值相同,比较检查的结果仍为false,并且效果得以运行。

答案 1 :(得分:1)

useEffect使用引用相等,这意味着[1,2,3]将不等于[1,2,3]

const data = [{id:1}];
data.map(d=>d.id)===data.map(d=>d.id);//is false

防止数组的id更改引用的一种方法是将useState与App中的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;
}