This question is related to this previous question of mine. I have written two custom hooks. The first is useFetchAccounts whose job is to fetch some data about user accounts and looks like this:
// 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
}
Nothing special about this hook, simply fetches the ids of the accounts.
Now I have another hook, useFetchBalancesWithIDs(account_ids, accounts_loaded), which is meant to take the ids from the previous step and fetch the balances of these accounts if the accounts have been loaded. It looks like this:
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
}
As you can see if the accounts_loaded is false, it will not perform the fetch. Together I am using them as follows:
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
}
Unfortunately, this results in an infinite loop. What does work is changing the useFetchBalancesWithIDs to this:
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
}
which performs the ids extraction within. I'm using it like this:
function App() {
const account_data = useFetchAccounts()
const accounts = account_data.accounts
const balance = useFetchBalanceWithAccounts(accounts, account_data.loaded)
console.log(balance)
return null
}
which runs just fine. So the problem seems to be related to the extraction of the IDs from within the App component, which seems to be triggering the useFetchBalanceWithIDs all the time, even though the account_ids do not change value. Can someone help explain this behaviour please? It's OK for me to use useFetchBalanceWithAccounts but I would like to understand why useFetchBalanceWithIDs doens't work. Thank you!
The issue is that you're using the map function to get an array of your account ids. React uses Object.is to determine if your values in the dependency array has changed in each render cycle. The map function returns a new array, so even though the values in your array are the same, the comparison check evaluates to false, and the effect is run.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With