在重构代码以获取帐户信息时,出现了页面无限期重新渲染的问题。
这是我用来获取帐户信息的自定义 Hook 。
//flattenTree returns an array of flattened Object that has children.
const flattenTree = <T extends { children: T[] }>(treeObject: T) => {
const flattenedTree: T[] = []
flattenedTree.push(treeObject)
const queue = [treeObject]
while (queue.length !== 0) {
const searching = queue.shift()
for (const child of searching?.children || []) {
flattenedTree.push(child)
queue.push(child)
}
}
return flattenedTree
}
const useFetchAccounts = () => {
const [accountsA, setAccountsA] = useState([])
const [accountsB, setAccountsB] = useState([])
const [accountIdsA, setAccountIdsA] = useState(new Set())
const [accountIdsB, setAccountIdsB] = useState(new Set())
const { data: accountTreeA } = useSWR(
`${FETCH_ACCOUNT_URL}/${ACCOUNTA_ID}`,
fetchAccount
)
const { data: accountTreeB } = useSWR(
`${FETCH_ACCOUNT_URL}/${ACCOUNTB_ID}`,
fetchAccount
)
useEffect(() => {
if (accountTreeA) {
setAccountsA(flattenTree(accountTreeA))
}
}, [accountTreeA])
useEffect(() => {
if (accountTreeB) {
setAccountsB(flattenTree(accountTreeB))
}
}, [accountTreeB])
useEffect(() => {
setAccountIdsA(new Set(accountsA.map((account) => account.accountId)))
}, [accountsA])
useEffect(() => {
setAccountIdsB(new Set(accountsB.map((account) => account.accountId)))
}, [accountsB])
return {
accounts: {
accountsA,
accountsB,
},
accountIds: {
accountIdsA,
accountIdsB,
},
}
}
这是使用上面自定义钩子(Hook)的组件。
const TestPage: React.FC = () => {
const [accountsC, setAccountsC] = useState<Account[]>([])
const [accountIdsC, setAccountIdsC] = useState<Set<string>>(new Set())
const [pageC, setPageC] = useState<LongShortPage[]>([])
const { accounts, accountIds } = useFetchAccounts()
const { data: pageA } = useSWR(
{
url: FETCH_BOOK_BY_ACCOUNT_ID,
targetAccountIds: [...accountIds.accountIdsA],
},
fetchPagesByAccountIds
)
useEffect(() => {
const { accountsA } = accounts
const { accountIdsA, accountIdsB } = accountIds
setAccountsC(accountsA.filter((account) => !accountIdsB.has(account.accountId)))
setAccountIdsC(
new Set([...accountIdsA].filter((accountId) => !accountIdsB.has(accountId)))
)
}, [accounts, accountIds])
useEffect(() => {
if (pageA) {
setPageC(pageA.filter((page) => accountIdsC.has(page.accountId)))
}
}, [pageA, accountIdsC])
return (
<li>
{[...accountIdsC].map((id) => (
<div key={id}>${id}</div>
))}
{/*
elision
*/}
</li>
)
}
似乎 useFetchAccounts 被无限调用,因此我的组件无限渲染。
于是,我以为自定义hook的使用方式有问题,于是将自定义hook内部的所有逻辑都移到了组件中,问题就解决了。
为什么会发生这种情况?
如何修改或重构代码?
这是一个重现该问题的codesandbox示例。
最佳答案
您可以使用useMemo
来记住这些值。当您使用 {} 返回一个对象时,它将假定该对象已更改到新位置。
return useMemo(
() => ({
accounts: {
accountsA,
accountsB,
},
accountIds: {
accountIdsA,
accountIdsB,
},
}), [accountsA, accountsB, accountIdsA, accountIdsB]);
更新相关评论的答案。
关于javascript - 与 useEffect 一起使用时,自定义钩子(Hook)会被无限调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73982780/