我应该删除 React 应用程序中所有不必要的 usecallback 和 memo 用法吗?
Should I remove all unecessary usage of usecallback and memo in a React app?
我在一个相对较大的 React 代码库上工作,我看到以前的开发人员大量使用 memo
和 usecallback
,认为使用它们可以提高性能。显然不是,这里举个例子
export default function QuestionHelper({ text }: { text: string }) {
const [show, setShow] = useState<boolean>(false);
const open = useCallback(() => setShow(true), [setShow]);
const close = useCallback(() => setShow(false), [setShow]);
return (
<span style={{ marginLeft: 4 }}>
<Tooltip show={show} text={text}>
<QuestionWrapper
onClick={open}
onMouseEnter={open}
onMouseLeave={close}
>
<Question size={16} />
</QuestionWrapper>
</Tooltip>
</span>
);
}
该应用程序现在可以正常工作。但是我拒绝修复它,因为该应用程序按预期工作。我怎样才能证明我们应该删除代码中所有不必要的 memo
和 usecallback
?
是否有 objective 方式来说明去除这些的好处?
因此,删除示例中的那个实际上可能会改变一些事情。如果您使用 useCallback
记忆它,那么 open
函数会在渲染之间持续存在,这意味着该函数在每个渲染周期都在内存中保持相同的位置。如果你删除它并且刚刚
const open = () => setShow(true);
然后每次组件渲染时,这个函数就是re-created,从而在内存中有一个新的位置。
这意味着什么?好吧,假设您的 QuestionWrapper
组件导出包含在 React.memo
中,因为不必要的 re-renders 可能会导致此特定组件出现性能问题。好吧,当比较 functions 和 ===
时,Javascript 如何比较对象(函数在技术上是 JS 中的对象)?它进行“引用比较”,即比较两个输入在内存中的地址,看它是否是同一个对象。在当前代码中,当使用 useCallback
记忆时,引用不会在 re-renders 之间改变。但是如果你删除它,那么它的引用将每 re-render 改变一次,并且 React.memo
将不再适用于 QuestionWrapper
因为它会认为它每次都获得一个新对象,即使实际功能及其逻辑永远不会改变。
现在,也许在您的应用程序中这无关紧要,也许 QuestionWrapper
可以 re-render 很多次而不会出现性能问题,或者也许该组件没有被记住,所以它不会很重要,但总的来说,重点是:不要尝试 refactor/optimize 不会引起问题的事情,除非 会有 大 不做的后果。
在理想情况下,您会评估这种情况,是的,从技术上讲,删除一些记忆可能是最佳选择。然而在 真实的 世界中,您继承的代码库充满了不理想的废话,但您只是出于多种原因使用它,例如:
- 虽然您认为这可能无关紧要,但根据我给出的示例,它有可能 可能 破坏事物或引入错误。安全总比后悔好
- 你一天只有固定的时间,所以你应该优先考虑更有影响力的工作
- 你从重构中获得的“改进”小到可以忽略不计,何必呢
以及其他各种原因。我知道 sub-optimal 的东西可能有点烦人,我们希望我们的代码超级流畅,well-structured 和优化,但不幸的是我们必须处理大的现实,real-world 应用程序代码通常充满了这样的东西。其中一些可能只是其他开发人员的懒惰或无知,或者其中一些实际上可能是出于一个可能不会立即显而易见的充分理由。
您总是可以自由表达担忧,但除非您非常有经验并完全理解此类重构的含义,并且字面上没有更好的事可做,那么如果每个人都在说就让它保持原样,最好这样做。几乎可以肯定,你的时间会得到更好的利用,如果你做了一些事情,比如不小心破坏了应用程序或引入了错误,只是为了进行微小的优化或修复甚至不会引起任何问题的事情,你会让自己变得非常不受欢迎。
此外,根据我使用 React 的经验,bug 很重要。记忆化可能会导致奇怪的行为,因此很难追踪。
我在一个相对较大的 React 代码库上工作,我看到以前的开发人员大量使用 memo
和 usecallback
,认为使用它们可以提高性能。显然不是,这里举个例子
export default function QuestionHelper({ text }: { text: string }) {
const [show, setShow] = useState<boolean>(false);
const open = useCallback(() => setShow(true), [setShow]);
const close = useCallback(() => setShow(false), [setShow]);
return (
<span style={{ marginLeft: 4 }}>
<Tooltip show={show} text={text}>
<QuestionWrapper
onClick={open}
onMouseEnter={open}
onMouseLeave={close}
>
<Question size={16} />
</QuestionWrapper>
</Tooltip>
</span>
);
}
该应用程序现在可以正常工作。但是我拒绝修复它,因为该应用程序按预期工作。我怎样才能证明我们应该删除代码中所有不必要的 memo
和 usecallback
?
是否有 objective 方式来说明去除这些的好处?
因此,删除示例中的那个实际上可能会改变一些事情。如果您使用 useCallback
记忆它,那么 open
函数会在渲染之间持续存在,这意味着该函数在每个渲染周期都在内存中保持相同的位置。如果你删除它并且刚刚
const open = () => setShow(true);
然后每次组件渲染时,这个函数就是re-created,从而在内存中有一个新的位置。
这意味着什么?好吧,假设您的 QuestionWrapper
组件导出包含在 React.memo
中,因为不必要的 re-renders 可能会导致此特定组件出现性能问题。好吧,当比较 functions 和 ===
时,Javascript 如何比较对象(函数在技术上是 JS 中的对象)?它进行“引用比较”,即比较两个输入在内存中的地址,看它是否是同一个对象。在当前代码中,当使用 useCallback
记忆时,引用不会在 re-renders 之间改变。但是如果你删除它,那么它的引用将每 re-render 改变一次,并且 React.memo
将不再适用于 QuestionWrapper
因为它会认为它每次都获得一个新对象,即使实际功能及其逻辑永远不会改变。
现在,也许在您的应用程序中这无关紧要,也许 QuestionWrapper
可以 re-render 很多次而不会出现性能问题,或者也许该组件没有被记住,所以它不会很重要,但总的来说,重点是:不要尝试 refactor/optimize 不会引起问题的事情,除非 会有 大 不做的后果。
在理想情况下,您会评估这种情况,是的,从技术上讲,删除一些记忆可能是最佳选择。然而在 真实的 世界中,您继承的代码库充满了不理想的废话,但您只是出于多种原因使用它,例如:
- 虽然您认为这可能无关紧要,但根据我给出的示例,它有可能 可能 破坏事物或引入错误。安全总比后悔好
- 你一天只有固定的时间,所以你应该优先考虑更有影响力的工作
- 你从重构中获得的“改进”小到可以忽略不计,何必呢
以及其他各种原因。我知道 sub-optimal 的东西可能有点烦人,我们希望我们的代码超级流畅,well-structured 和优化,但不幸的是我们必须处理大的现实,real-world 应用程序代码通常充满了这样的东西。其中一些可能只是其他开发人员的懒惰或无知,或者其中一些实际上可能是出于一个可能不会立即显而易见的充分理由。
您总是可以自由表达担忧,但除非您非常有经验并完全理解此类重构的含义,并且字面上没有更好的事可做,那么如果每个人都在说就让它保持原样,最好这样做。几乎可以肯定,你的时间会得到更好的利用,如果你做了一些事情,比如不小心破坏了应用程序或引入了错误,只是为了进行微小的优化或修复甚至不会引起任何问题的事情,你会让自己变得非常不受欢迎。
此外,根据我使用 React 的经验,bug 很重要。记忆化可能会导致奇怪的行为,因此很难追踪。