简体   繁体   English

在 React.memo 中需要 useCallback 吗?

[英]useCallback necessary within React.memo?

Consider this example:考虑这个例子:

import React, { useCallback } from 'react';

type UserInputProps = {
  onChange: (value: string) => void;
};

const UserInput = React.memo(({ onChange }: UserInputProps) => {
  // Is this `useCallback` redundant?
  const handleChange = useCallback(
    (event) => {
      onChange(event.target.value);
    },
    [onChange]
  );

  return <input type="text" onChange={handleChange} />;
});

export default UserInput;

My questions are:我的问题是:

  1. When the props consist of only onChange and no other elements, is the useCallback unnecessary in this case, since the entire component is already memo-ed based on onChange ?当 props 仅包含onChange而没有其他元素时,在这种情况下是否不需要useCallback ,因为整个组件已经基于onChange进行了备忘录?
  2. If we add an additional prop (say a value for the initial value for the <input> ), then I think useCallback becomes useful, since otherwise handleChange will be recreated even if onChange doesn't change but value is changed.如果我们添加一个额外的道具(比如<input>的初始value的值),那么我认为useCallback变得有用,因为否则即使onChange没有改变但value改变了,也会重新创建handleChange Is that correct?那是对的吗?

When the props contains only onChange and no other elements, is the useCallback unnecessary in this case, since the entire component is already memo-ed based on onChange ?当 props 仅包含onChange而没有其他元素时,在这种情况下是否不需要useCallback ,因为整个组件已经基于onChange进行了备忘录?

It's memoized based on all props, not just onChange .它是基于所有道具记忆的,而不仅仅是onChange Yes, useCallback is unnecessary there.是的, useCallback在那里是不必要的。

If we add an additional prop (say a value for the initial value for the <input> ), then I think useCallback becomes useful, since otherwise handleChange will be recreated even if onChange doesn't change but value is changed.如果我们添加一个额外的道具(比如<input>的初始value的值),那么我认为useCallback变得有用,因为否则即使onChange没有改变但值改变了,也会重新创建handleChange Is that correct?那是对的吗?

If you want to update value and not update onChange when updating the input , you'd add value to your props and continue to use useCallback for your handleChange (if you like, so that React doesn't have to swap in a new event handler when it's setting the value; my impression is that's often overkill).如果你想在更新input时更新value而不是更新onChange ,你可以为你的 props 添加value并继续为你的handleChange useCallback如果你愿意,这样 React 就不必换入新的事件处理程序当它设置值时;我的印象是这通常是矫枉过正的)。 Eg:例如:

const UserInput = React.memo(({ onChange, value }: UserInputProps) => {
  const handleChange = useCallback(
    (event) => {
      onChange(event.target.value);
    },
    [onChange]
  );

  return <input type="text" value={value} onChange={handleChange} />;
});

That will use the latest value and reuse the previous handleChange if the onChange prop hasn't changed.如果onChange道具没有改变,那将使用最新的value并重用之前的handleChange

You might not keep React.memo in that case, if you're expecting that value will get changed as a result of the onChange .在这种情况下,您可能不会保留React.memo ,如果您期望该value会因onChange而改变。 That is, if you expect the common case to be that value will be different each time your component is called, then React.memo 's check of the props is extra work you might not keep:也就是说,如果您希望每次调用组件时该value都会有所不同,那么React.memo对 props 的检查是您可能不会保留的额外工作:

const UserInput = ({ onChange, value }: UserInputProps) => {
  const handleChange = useCallback(
    (event) => {
      onChange(event.target.value);
    },
    [onChange]
  );

  return <input type="text" value={value} onChange={handleChange} />;
};

But you can keep it if you want React.memo to keep checking the props and not call your function when neither prop changes.但是如果你希望React.memo继续检查 props 并且在 props 都没有变化时不调用你的 function,你可以保留它。

If you just want to supply the initial value to your component and then control it locally, you could continue to use React.memo (since it still renders the same thing for the same input props), in which case you don't need useCallback :如果你只是想为你的组件提供初始值然后在本地控制它,你可以继续使用React.memo (因为它仍然为相同的输入道具呈现相同的东西),在这种情况下你不需要useCallback

const UserInput = React.memo(({ onChange, initialValue }: UserInputProps) => {
  const [value, setValue] = useState(initialValue);
  const handleChange = (event) => {
    setValue(event.target.value);
    onChange(event.target.value);
  };

  return <input type="text" value={value} onChange={handleChange} />;
});

That will only get called when onChange or initialValue changes.只有当onChangeinitialValue改变时才会被调用。 You could use also useCallback there in order to, again, only update the onChange on the input when the onChange prop changes, to avoid having React remove the old handler and set the new one when just the value changes:您也可以在那里使用useCallback ,以便再次仅在onChange道具更改时更新input上的onChange ,以避免 React 删除旧处理程序并在value更改时设置新处理程序:

const UserInput = React.memo(({ onChange, initialValue }: UserInputProps) => {
  const [value, setValue] = useState(initialValue);
  const handleChange = useCallback(
    (event) => {
      setValue(event.target.value);
      onChange(event.target.value);
    },
    [onChange]
 );

  return <input type="text" value={value} onChange={handleChange} />;
});

Side note: One thing to remember is that a new handleChange function is created every time your component function is called, even when you're using useCallback .旁注:要记住的一件事是,每次调用组件 function 时都会创建一个新的handleChange function ,即使您使用的是useCallback It has to be, so it can be passed into useCallback as an argument.它必须是,所以它可以作为参数传递给useCallback The only difference is whether you use that new function, or the original one that was created the first time (the result of useCallback ).唯一的区别是您是使用新的 function,还是第一次创建的原始的( useCallback的结果)。 I think the reason for reusing the first one that was created for a given set of dependencies is to minimize changes passed to child components.我认为重用为给定的一组依赖项创建的第一个依赖项的原因是最小化传递给子组件的更改。

When the props consist of only onChange and no other elements, is the useCallback unnecessary in this case , since the entire component is already memo-ed based on onChange?当 props 仅包含onChange而没有其他元素时,在这种情况下是否不需要useCallback ,因为整个组件已经基于 onChange 进行了备忘录?

No, it might be necessary , memo and useCallback don't serve the same purpose.不,可能有必要memouseCallback的目的不同。

Without useCallback you may have "heavy computation" on every render:如果没有useCallback ,您可能在每次渲染时都有“大量计算”:

"heavy computation" refers to a generic case and not to this specific example where you only pass the event's value. “大量计算”是指一般情况,而不是仅传递事件值的特定示例。

const UserInput = React.memo(({ onChange = () => {} }) => {
  // Uncomment for memoization
  // Note that you can implement useCallback with useMemo

  // const handleChange = useMemo(() => {
  //   console.log("heavy computation memoized");
  //   return event => {
  //     onChange(event.target.value);
  //   };
  // }, [onChange]);

  const handleChange = event => {
    // Here we can have some heavy computation
    // Not related to this specific usecase
    console.log("heavy computation on every render");
    onChange(event.target.value);
  };

  return <input type="text" onChange={handleChange} />;
});

If we add an additional prop (say a value for the initial value for the ), then I think useCallback becomes useful, since otherwise handleChange will be recreated even if onChange doesn't change but value is changed.如果我们添加一个额外的道具(比如 的初始值的值),那么我认为useCallback变得有用,因为否则即使 onChange 没有改变但值改变了,也会重新创建handleChange Is that correct?那是对的吗?

If you are going to use controlled component (due to use of value prop of input ), the initialValue will be initialized once , so it pretty useless trying to memorize it (for what purpose?):如果您要使用受控组件(由于使用inputvalue属性),initialValue将被初始化一次,因此尝试记住它是毫无用处的(出于什么目的?):

const UserInputWithValue = ({ onChange, initialValue }) => {
  // initilized once, no need to memoize
  const [value,setValue] = useState(initialValue);
  const handleChange = useCallback(
    (event) => {
      setValue(event.target.value)
      onChange(event.target.value);
    },
    [onChange]
  );

  // Controlled
  return <input type="text" value={value} onChange={handleChange} />;
};

编辑objective-brown-nyuyc

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

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