我想开始讨论创建回调的推荐方法,该回调从循环内创建的组件中获取参数。
例如,如果我正在填充将具有“删除”按钮的项目列表,我希望“onDeleteItem”回调知道要删除的项目的索引。所以是这样的:
const onDeleteItem = useCallback(index => () => {
setList(list.slice(0, index).concat(list.slice(index + 1)));
}, [list]);
return (
<div>
{list.map((item, index) =>
<div>
<span>{item}</span>
<button type="button" onClick={onDeleteItem(index)}>Delete</button>
</div>
)}
</div>
);
但是这样做的问题是 onDeleteItem 总是会向 onClick 处理程序返回一个新函数,从而导致重新呈现按钮,即使列表没有更改也是如此。所以它违背了useCallback
.
我想出了我自己的钩子,我称之为useLoopCallback,它通过将主回调以及循环参数映射到他们自己的回调来解决问题:
import React, {useCallback, useMemo} from "react";
export function useLoopCallback(code, dependencies) {
const callback = useCallback(code, dependencies);
const loopCallbacks = useMemo(() => ({map: new Map(), callback}), [callback]);
return useCallback(loopParam => {
let loopCallback = loopCallbacks.map.get(loopParam);
if (!loopCallback) {
loopCallback = (...otherParams) => loopCallbacks.callback(loopParam, ...otherParams);
loopCallbacks.map.set(loopParam, loopCallback);
}
return loopCallback;
}, [callback]);
}
所以现在上面的处理程序看起来像这样:
const onDeleteItem = useLoopCallback(index => {
setList(list.slice(0, index).concat(list.slice(index + 1)));
}, [list]);
这很好,但现在我想知道这个额外的逻辑是否真的让事情变得更快或者只是增加了不必要的开销。谁能提供一些见解?
编辑: 上述方法的替代方法是将列表项包装在它们自己的组件中。所以是这样的:
function ListItem({key, item, onDeleteItem}) {
const onDelete = useCallback(() => {
onDeleteItem(key);
}, [onDeleteItem, key]);
return (
<div>
<span>{item}</span>
<button type="button" onClick={onDelete}>Delete</button>
</div>
);
}
export default function List(...) {
...
const onDeleteItem = useCallback(index => {
setList(list.slice(0, index).concat(list.slice(index + 1)));
}, [list]);
return (
<div>
{list.map((item, index) =>
<ListItem key={index} item={item} onDeleteItem={onDeleteItem} />
)}
</div>
);
}