用于地图渲染的 React useCallback 钩子
React useCallback hook for map rendering
将回调传递给组件时,我应该使用 useCallback
挂钩 return 记忆回调(以防止不必要的渲染):
import doSomething from "./doSomething";
const FrequentlyRerenders = ({ id }) => {
const onEvent = useCallback(() => doSomething(id), [id]);
return (
<ExpensiveComponent onEvent={ onEvent } />
);
};
但是如果我使用地图呢?例如:
import doSomething from "./doSomething";
const FrequentlyRerendersMap = ({ ids }) => {
return ids.map(id => {
const onEvent = useCallback(() => doSomething(id), [id]);
return (
<ExpensiveComponent key={id} onEvent={ onEvent } />
);
});
};
如何正确使用useCallback
?以上是传递多个回调的正确方法吗?它真的有效并且知道根据数组的一项来记忆每个回调吗?
将返回的映射JSX转换为组件,然后就可以毫无问题地使用Callback
import doSomething from "./doSomething";
const MappedComponent =(props) => {
const onEvent = useCallback(() => doSomething(props.id), []);
return (
<ExpensiveComponent onEvent={ onEvent } />
);
}
const FrequentlyRerendersMap = ({ ids }) => {
return ids.map(id => {
return <MappedComponent key={id} id={id} />
});
};
现在文档中明确不鼓励这样做。
https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
旧答案
重新架构的答案是回避 IMO 的问题。不过,我认为创建一个新组件可能是个好主意。
不过要回答这个问题,您的代码:
import doSomething from "./doSomething";
const FrequentlyRerendersMap = ({ ids }) => {
return ids.map(id => {
const onEvent = useCallback(() => doSomething(id), [id]);
return (
<ExpensiveComponent key={id} onEvent={ onEvent } />
);
});
};
这实际上是您想在地图中记忆的方法。我不知道 useCallback 的实现,但它应该增加很少的内存开销。 stackFrame,以及他们为将数组缩减为记忆函数的某种键所做的一切。
除非您使用大量元素来处理某些内容,否则 EG 会反应虚拟化组件无限滚动,否则您实际上可以安全地按自己的方式使用回调。事实上,与重新渲染所有这些组件相比,较小的内存开销可能要便宜得多。
还有一个选择:
import doSomething from "./doSomething";
const FrequentlyRerendersMap = ({ ids }) => {
const onEvent = useCallback((id) => () => doSomething(id), []);
return ids.map(id => {
return (
<ExpensiveComponent key={id} onEvent={ onEvent: onEvent(id) } />
);
});
};
将回调传递给组件时,我应该使用 useCallback
挂钩 return 记忆回调(以防止不必要的渲染):
import doSomething from "./doSomething";
const FrequentlyRerenders = ({ id }) => {
const onEvent = useCallback(() => doSomething(id), [id]);
return (
<ExpensiveComponent onEvent={ onEvent } />
);
};
但是如果我使用地图呢?例如:
import doSomething from "./doSomething";
const FrequentlyRerendersMap = ({ ids }) => {
return ids.map(id => {
const onEvent = useCallback(() => doSomething(id), [id]);
return (
<ExpensiveComponent key={id} onEvent={ onEvent } />
);
});
};
如何正确使用useCallback
?以上是传递多个回调的正确方法吗?它真的有效并且知道根据数组的一项来记忆每个回调吗?
将返回的映射JSX转换为组件,然后就可以毫无问题地使用Callback
import doSomething from "./doSomething";
const MappedComponent =(props) => {
const onEvent = useCallback(() => doSomething(props.id), []);
return (
<ExpensiveComponent onEvent={ onEvent } />
);
}
const FrequentlyRerendersMap = ({ ids }) => {
return ids.map(id => {
return <MappedComponent key={id} id={id} />
});
};
现在文档中明确不鼓励这样做。 https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
旧答案
重新架构的答案是回避 IMO 的问题。不过,我认为创建一个新组件可能是个好主意。
不过要回答这个问题,您的代码:
import doSomething from "./doSomething";
const FrequentlyRerendersMap = ({ ids }) => {
return ids.map(id => {
const onEvent = useCallback(() => doSomething(id), [id]);
return (
<ExpensiveComponent key={id} onEvent={ onEvent } />
);
});
};
这实际上是您想在地图中记忆的方法。我不知道 useCallback 的实现,但它应该增加很少的内存开销。 stackFrame,以及他们为将数组缩减为记忆函数的某种键所做的一切。
除非您使用大量元素来处理某些内容,否则 EG 会反应虚拟化组件无限滚动,否则您实际上可以安全地按自己的方式使用回调。事实上,与重新渲染所有这些组件相比,较小的内存开销可能要便宜得多。
还有一个选择:
import doSomething from "./doSomething";
const FrequentlyRerendersMap = ({ ids }) => {
const onEvent = useCallback((id) => () => doSomething(id), []);
return ids.map(id => {
return (
<ExpensiveComponent key={id} onEvent={ onEvent: onEvent(id) } />
);
});
};