简体   繁体   English

为什么 React 的 useCallback 没有使用 ref 进行优化?

[英]Why isn't React's useCallback optimized with a ref?

TL;DR TL;博士

Why is useCallback defined as (roughly)为什么将useCallback定义为(大致)

function useCallback(callback, deps) {
  return useMemo((...args) => {
    return callback(...args);
  }, deps);
}

instead of like this?而不是这样?

function useCallback(callback) {
  const fn = useRef(callback);
  fn.current = callback;

  return useMemo((...args) => {
    return fn.current(...args);
  }, []);
}

It seems like it would solve unnecessary rerenders, while always working with the most recent version of the function.它似乎可以解决不必要的重新渲染,同时始终使用最新版本的 function。 I also heard that Vue 3 optimizes in this exact same way using the cacheHandlers option.我还听说 Vue 3 使用cacheHandlers选项以完全相同的方式进行优化。


Contextually explained version上下文解释版本

When writing react components/hooks/contexts, you can either just write functions directly:在编写 react 组件/钩子/上下文时,您可以直接编写函数:

const bootstrapAuth = async () => {
  // ...
};

…or optimize for minimal rerenders using useCallback : …或使用useCallback优化最小重新渲染:

const bootstrapAuth = useCallback(async () => {
  // ...
}, []);

Myself I tend to use useCallback often, but as a teacher, I don't teach my students this from the start.我自己倾向于经常使用useCallback ,但作为一名老师,我从一开始就不会教我的学生这个。 It's an extra complication, and is as far as.这是一个额外的并发症,而且是。 I know officially considered only a performance concern .我知道官方只考虑了性能问题 It's just useMemo , nothing more.它只是useMemo ,仅此而已。

But when you start combining effects and functions, it can become critical, such as in:但是当您开始组合效果和功能时,它可能变得至关重要,例如:

const bootstrapAuth = async () => {
  // ...
};
useEffect(() => {
  bootstrapAuth();
}, []);

^^ this is technically incorrect (eg the linter will complain), but then, putting bootstrapAuth in the dependencies array is even worse, because it will rerun on every render. ^^ 这在技术上是不正确的(例如 linter 会抱怨),但是,将bootstrapAuth放在依赖项数组中会更糟,因为它会在每次渲染时重新运行。 There seem to be three solutions:似乎有三种解决方案:

  1. Use useCallback . Use useCallback But that violates the principle that it's just a performance concern, and I don't see how this is not a widespread problem in the react community.但这违反了它只是性能问题的原则,我不明白这在 React 社区中不是一个普遍存在的问题。 I usually choose this approach.我通常选择这种方法。

  2. Make bootstrapAuth idempotent, ie running it more often than necessary doesn't cause additional effects.使bootstrapAuth具有幂等性,即比必要更频繁地运行它不会造成额外的影响。 Making functions idempotent is always smart, but it seems like a weird band-aid given that it totally defies the effect hook.使函数具有幂等性总是很聪明,但它似乎是一个奇怪的创可贴,因为它完全违背了效果挂钩。 Surely this isn't the general solution.当然,这不是一般的解决方案。

  3. Use a ref, like so (although the example is a bit contrived):使用 ref,像这样(虽然这个例子有点做作):

     const bootstrapAuthLatest = useRef(bootstrapAuth); bootstrapAuthLatest.current = bootstrapAuth; useEffect(() => { bootstrapAuthLatest.current(); }, []);

This last “trick”, although a bit contrived here, does seem to be a go-to approach in helper hook libraries, and I've been using it a lot myself, pretty much whenever I introduce a hook that accepts a callback.最后一个“技巧”虽然在这里有点做作,但似乎确实是辅助钩子库中的首选方法,而且我自己也经常使用它,几乎每当我引入一个接受回调的钩子时。 And it makes you wonder, why didn't the React team just define useCallback like so, in the very first place??这让你想知道,为什么 React 团队一开始没有像这样定义 useCallback ?

function useCallback(callback) {
  const fn = useRef(callback);
  fn.current = callback;

  return useMemo((...args) => {
    return fn.current(...args);
  }, []);
}

Myself I tend to use useCallback wherever, [...]我自己倾向于在任何地方使用 useCallback,[...]

const bootstrapAuth = useCallback(async () => {
  // ...
}, []);

is the same as是相同的

const bootstrapAuth = async () => {
  // ...
};
const bootstrapAuthCallBack = useCallback(bootstrapAuth, [])

You effectively created an additional function (compared to not using useCallback ), an array, allocated the respective memory and have useCallback set its properties and run through logical expressions.您有效地创建了一个额外的 function(与不使用useCallback相比)一个数组,分配了相应的 memory 并让useCallback设置其属性并运行逻辑表达式。 Wapping everything inside useCallback does not make all code run faster by default.默认情况下,在useCallback中交换所有内容不会使所有代码运行得更快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM