我应该用 useCallback 还是 useMemo 包装每个道具,什么时候使用这个钩子?
Should I wrap every prop with useCallback or useMemo, when to use this hooks?
有了现在可用的反应钩子,我是否应该在功能组件的情况下用 useCallback and every other props value with useMemo 包装每个通过 props 传递的函数?
在我的组件中也有自定义函数依赖于任何道具值我应该用 useCallback 包装它吗?
用这个钩子决定组件中的哪些 props 或 const 值有哪些好的做法?
如果这可以提高性能,为什么不一直这样做呢?
让我们考虑自定义按钮,我们在其中包装点击处理程序并添加自定义逻辑
function ExampleCustomButton({ onClick }) {
const handleClick = useCallback(
(event) => {
if (typeof onClick === 'function') {
onClick(event);
}
// do custom stuff
},
[onClick]
);
return <Button onClick={handleClick} />;
}
让我们考虑自定义按钮,我们在其中包装点击处理程序并根据条件添加自定义逻辑
function ExampleCustomButton({ someBool }) {
const handleClick = useCallback(
(event) => {
if (someBool) {
// do custom stuff
}
},
[someBool]
);
return <Button onClick={handleClick} />;
}
在这两种情况下,我应该用 useCallback 包装我的处理程序吗?
使用备忘录的类似案例。
function ExampleCustomButton({ someBool }) {
const memoizedSomeBool = useMemo(() => someBool, [someBool])
const handleClick = useCallback(
(event) => {
if (memoizedSomeBool) {
// do custom stuff
}
},
[memoizedSomeBool]
);
return <Button onClick={handleClick} />;
}
在这个例子中,我什至将记忆值传递给 useCallback。
另一种情况,如果在组件树中有许多组件记忆相同的值怎么办?这对性能有何影响?
不值得,原因有很多:
- 甚至官方文档也说你应该只在必要时才这样做。
- 请记住,过早的优化是万恶之源 :)
- 它使 DX(开发人员体验)变得更糟:更难阅读;更难写;更难重构。
- 在处理原语时(就像在您的示例中),记忆比不记忆消耗更多的 CPU 能量。原始值没有 references 的概念,所以没有什么可以记忆的。另一方面,记忆化本身(与任何其他钩子一样)确实需要一些微小的处理,没有什么是免费的。尽管它很小,但它仍然比什么都重要(与仅通过图元相比),所以你会用这种方法搬起石头砸自己的脚。
总而言之 - 如果您想将它们放在应用程序中,您将浪费更多的时间来输入所有挂钩,而不是让用户将它们放在应用程序中。古老的规则适用:测量,然后优化。
同意@jalooc提出的原则
为了更深入地了解 OP 中展示的用例,我的建议如下:
昂贵的儿童渲染
function Component() {
const callback = useCallback(() => { dostuff }, [deps])
return <Child prop={callback} />
}
如果 Child
是一个渲染成本非常高的组件,那么上面的内容就有意义了。因此,它可能是这样导出的:
function Child() {
...this takes significant CPU...
}
// Export as a pure component
export default React.memo(Child)
昂贵的计算
function Component({ foo }) {
// This very expensive computation will only run when it's input (foo)
// changes, allowing Component to re-render without performance issues
const bar = useMemo(() => {
... something very complicated with `foo` ...
}, [foo])
return <div>{bar}</div>
}
结论
- 做有意义的事情或衡量表现不佳的事情
- 组件内的函数声明在每次渲染时都会发生变化。如果这会导致派生的昂贵计算,请记住它 (
useCallback
) 或将其移出范围。
- 如果组件本身的渲染成本很高,请使用
React.memo
使其纯净,必要时借助 #2
- 如果重新计算某些东西本身很昂贵,请记住它 (
useMemo
)
- 做有意义的事情或衡量表现不佳的事情
我认为将 useCallback
放在你创建函数的任何地方都没有错。同样的要点也适用于useMemo
;但为了简洁起见,我只会提到 useCallback
。
作为让步,我必须指出,这不是您开发过程中的基石,您必须继续采用您的团队满意的解决方案,无论是否使用 useCallback
。
我“广泛”使用此类记忆挂钩的主要论点是,如果您这样做,则不必考虑“参考 update-induced”重新呈现前的潜在性能问题。更少的事情要考虑 = 好。更多的时间和精力去解决实际问题。
很多人说“过早的优化不好”。好吧,货物崇拜也不好,这个引文就是一个纯粹的例子,太脱离上下文了,而且前提是有权威支持。 here 曾经是一个很好的总结,不幸的是,该帐户目前已被删除,但它可以在 archive.org 上使用。
在这种情况下,“过早优化”的问题是代码的结构更改和受损 readability/write-ability。
在结构上,这些变化是为了更好,让你分离你的组件。
Readability-wise,有一个额外的包装器,但它伴随着对函数依赖性的严格跟踪,否则它是隐式完成的。依赖关系变得明确,因为它们应该如此,因为在这个系统中,它们扮演的角色太重要了,不能掩盖它。因此,可读性只会获胜。现在虽难写,但“礼”始终如一。可读性比编写函数的便利性更重要。
(只要确保您使用 eslint
来跟踪您的依赖项;当您忘记添加依赖项并且它被缓存时,这可能会让人头疼。)
因此,“现在编写,稍后优化”- 谢谢,我会通过。对我来说,如果您的团队准备好接受上述论点,那么这是一个可以忽略不计的优化,足够合理且足够合理。如果他们不是,那也没关系:主题本身不是值得为之牺牲的东西。这只是一个小 quality-of-life 工具。
有了现在可用的反应钩子,我是否应该在功能组件的情况下用 useCallback and every other props value with useMemo 包装每个通过 props 传递的函数?
在我的组件中也有自定义函数依赖于任何道具值我应该用 useCallback 包装它吗?
用这个钩子决定组件中的哪些 props 或 const 值有哪些好的做法?
如果这可以提高性能,为什么不一直这样做呢?
让我们考虑自定义按钮,我们在其中包装点击处理程序并添加自定义逻辑
function ExampleCustomButton({ onClick }) {
const handleClick = useCallback(
(event) => {
if (typeof onClick === 'function') {
onClick(event);
}
// do custom stuff
},
[onClick]
);
return <Button onClick={handleClick} />;
}
让我们考虑自定义按钮,我们在其中包装点击处理程序并根据条件添加自定义逻辑
function ExampleCustomButton({ someBool }) {
const handleClick = useCallback(
(event) => {
if (someBool) {
// do custom stuff
}
},
[someBool]
);
return <Button onClick={handleClick} />;
}
在这两种情况下,我应该用 useCallback 包装我的处理程序吗?
使用备忘录的类似案例。
function ExampleCustomButton({ someBool }) {
const memoizedSomeBool = useMemo(() => someBool, [someBool])
const handleClick = useCallback(
(event) => {
if (memoizedSomeBool) {
// do custom stuff
}
},
[memoizedSomeBool]
);
return <Button onClick={handleClick} />;
}
在这个例子中,我什至将记忆值传递给 useCallback。
另一种情况,如果在组件树中有许多组件记忆相同的值怎么办?这对性能有何影响?
不值得,原因有很多:
- 甚至官方文档也说你应该只在必要时才这样做。
- 请记住,过早的优化是万恶之源 :)
- 它使 DX(开发人员体验)变得更糟:更难阅读;更难写;更难重构。
- 在处理原语时(就像在您的示例中),记忆比不记忆消耗更多的 CPU 能量。原始值没有 references 的概念,所以没有什么可以记忆的。另一方面,记忆化本身(与任何其他钩子一样)确实需要一些微小的处理,没有什么是免费的。尽管它很小,但它仍然比什么都重要(与仅通过图元相比),所以你会用这种方法搬起石头砸自己的脚。
总而言之 - 如果您想将它们放在应用程序中,您将浪费更多的时间来输入所有挂钩,而不是让用户将它们放在应用程序中。古老的规则适用:测量,然后优化。
同意@jalooc提出的原则
为了更深入地了解 OP 中展示的用例,我的建议如下:
昂贵的儿童渲染
function Component() {
const callback = useCallback(() => { dostuff }, [deps])
return <Child prop={callback} />
}
如果 Child
是一个渲染成本非常高的组件,那么上面的内容就有意义了。因此,它可能是这样导出的:
function Child() {
...this takes significant CPU...
}
// Export as a pure component
export default React.memo(Child)
昂贵的计算
function Component({ foo }) {
// This very expensive computation will only run when it's input (foo)
// changes, allowing Component to re-render without performance issues
const bar = useMemo(() => {
... something very complicated with `foo` ...
}, [foo])
return <div>{bar}</div>
}
结论
- 做有意义的事情或衡量表现不佳的事情
- 组件内的函数声明在每次渲染时都会发生变化。如果这会导致派生的昂贵计算,请记住它 (
useCallback
) 或将其移出范围。 - 如果组件本身的渲染成本很高,请使用
React.memo
使其纯净,必要时借助#2
- 如果重新计算某些东西本身很昂贵,请记住它 (
useMemo
) - 做有意义的事情或衡量表现不佳的事情
我认为将 useCallback
放在你创建函数的任何地方都没有错。同样的要点也适用于useMemo
;但为了简洁起见,我只会提到 useCallback
。
作为让步,我必须指出,这不是您开发过程中的基石,您必须继续采用您的团队满意的解决方案,无论是否使用 useCallback
。
我“广泛”使用此类记忆挂钩的主要论点是,如果您这样做,则不必考虑“参考 update-induced”重新呈现前的潜在性能问题。更少的事情要考虑 = 好。更多的时间和精力去解决实际问题。
很多人说“过早的优化不好”。好吧,货物崇拜也不好,这个引文就是一个纯粹的例子,太脱离上下文了,而且前提是有权威支持。 here 曾经是一个很好的总结,不幸的是,该帐户目前已被删除,但它可以在 archive.org 上使用。
在这种情况下,“过早优化”的问题是代码的结构更改和受损 readability/write-ability。
在结构上,这些变化是为了更好,让你分离你的组件。
Readability-wise,有一个额外的包装器,但它伴随着对函数依赖性的严格跟踪,否则它是隐式完成的。依赖关系变得明确,因为它们应该如此,因为在这个系统中,它们扮演的角色太重要了,不能掩盖它。因此,可读性只会获胜。现在虽难写,但“礼”始终如一。可读性比编写函数的便利性更重要。
(只要确保您使用 eslint
来跟踪您的依赖项;当您忘记添加依赖项并且它被缓存时,这可能会让人头疼。)
因此,“现在编写,稍后优化”- 谢谢,我会通过。对我来说,如果您的团队准备好接受上述论点,那么这是一个可以忽略不计的优化,足够合理且足够合理。如果他们不是,那也没关系:主题本身不是值得为之牺牲的东西。这只是一个小 quality-of-life 工具。