我一直很喜欢陷入困境,并处理与现实问题相关的所有新的有趣问题:)这是我遇到过几次,很想看看你的“应该”解决!
概述:我创建了一个自定义钩子,以封装应用程序的某些业务逻辑并存储一些状态。我在组件内部使用了该自定义钩子,并在加载时触发了一个事件。
问题是:我的钩子的loadItems
函数需要访问我的items
才能获取最后一个项目的ID。将items
添加到我的依赖项数组会导致无限循环。这是一个(简化的)示例:
简单的ItemList
组件
//
// Simple functional component
//
import React, { useEffect } from 'react'
import useItems from '/path/to/custom/hooks/useItems'
const ItemList = () => {
const { items, loadItems } = useItems()
// On load, use our custom hook to fire off an API call
// NOTE: This is where the problem lies. Since in our hook (below)
// we rely on `items` to set some params for our API, when items changes
// `loadItems` will also change, firing off this `useEffect` call again.. and again :)
useEffect(() => {
loadItems()
}, [loadItems])
return (
<ul>
{items.map(item => <li>{item.text}</li>)}
</ul>
)
}
export default ItemList
自定义useItems
挂钩
//
// Simple custom hook
//
import { useState, useCallback } from 'react'
const useItems = () => {
const [items, setItems] = useState([])
// NOTE: Part two of where the problem comes into play. Since I'm using `items`
// to grab the last item's id, I need to supply that as a dependency to the `loadItems`
// call per linting (and React docs) instructions. But of course, I'm setting items in
// this... so every time this is run it will also update.
const loadItems = useCallback(() => {
// Grab our last item
const lastItem = items[items.length - 1]
// Supply that item's id to our API so we can paginate
const params = {
itemsAfter: lastItem ? lastItem.id : nil
}
// Now hit our API and update our items
return Api.fetchItems(params).then(response => setItems(response.data))
}, [items])
return { items, loadItems }
}
export default useItems
代码中的注释应该指出问题所在,但是我现在想出的使短毛猫高兴的唯一解决方案是向loadItems
调用(例如loadItems({ itemsAfter: ... })
)提供参数。因为数据已经在这个自定义钩子中了,所以我真的希望不必在使用loadItems
函数的任何地方都要做。
非常感谢您的帮助!
迈克
答案 0 :(得分:1)
如果您打算只运行一次效果,请忽略所有依赖项:
useEffect(() => {
loadItems();
}, []);
答案 1 :(得分:0)
您可以尝试使用useReducer,将分发作为loadItems传递,因为它永远不会更改引用。减速器只关心操作是否为NONE,因为那是useEffect的清理功能所做的清理。
如果动作不是NONE,则状态将被设置为最后一个项目,这将触发useEffect使用您的api进行获取,当解析后它将使用setItems来设置项目。
const NONE = {};
const useItems = () => {
const [items, setItems] = useState([]);
const [lastItem, dispatch] = useReducer(
(state, action) => {
return action === NONE
? NONE
: items[items.length - 1];
},
NONE
);
useEffect(() => {
//initial useEffect or after cleanup, do nothing
if (lastItem === NONE) {
return;
}
const params = {
itemsAfter: lastItem ? lastItem.id : Nil,
};
// Now hit our API and update our items
Api.fetchItems(params).then(response =>
setItems(response)
);
return () => dispatch(NONE); //clean up
}, [lastItem]);
//return dispatch as load items, it'll set lastItem and trigger
// the useEffect
return { items, loadItems: dispatch };
};