在React.memo中有必要使用useCallback吗?

时间:2020-07-09 09:09:46

标签: javascript reactjs typescript

考虑以下示例:

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;

我的问题是:

  1. 当道具仅包含onChange而没有其他元素时,在这种情况下useCallback是不必要的,因为整个组件已经基于onChange进行了记录吗? li>
  2. 如果我们添加一个额外的道具(例如,value的初始值是<input>),那么我认为useCallback会很有用,因为否则handleChange将即使onChange不变但value也被重新创建。正确吗?

2 个答案:

答案 0 :(得分:8)

当道具仅包含onChange而没有其他元素时,在这种情况下useCallback是不必要的,因为整个组件已经基于onChange进行了记录吗?

它是基于所有道具而不仅仅是onChange来记忆的。是的,useCallback在那里是不需要的。

如果我们添加一个额外的道具(例如,value的初始值是<input>),那么我认为useCallback很有用,因为否则handleChange将即使onChange不变,但值已更改,也可以重新创建。正确吗?

如果您想在更新value时更新onChange而不更新input,则可以在道具中添加value并继续使用{{1} }作为useCallback的名称(如果愿意,这样React可以在设置值时不必交换新的事件处理程序;我的印象是,这通常是过大的)。例如:

handleChange

这将使用最新的const UserInput = React.memo(({ onChange, value }: UserInputProps) => { const handleChange = useCallback( (event) => { onChange(event.target.value); }, [onChange] ); return <input type="text" value={value} onChange={handleChange} />; }); 并在value道具未更改的情况下重用先前的handleChange

在这种情况下,如果您期望onChange由于React.memo而被更改,则可能不会保留value。也就是说,如果您希望常见的情况是每次调用您的组件时onChange 都会不同,那么value对道具的检查就是您的额外工作可能不会保留:

React.memo

但是如果您希望const UserInput = ({ onChange, value }: UserInputProps) => { const handleChange = useCallback( (event) => { onChange(event.target.value); }, [onChange] ); return <input type="text" value={value} onChange={handleChange} />; }; 继续检查道具而不在道具没有变化时不调用函数,则可以保留它。

如果您只是要向组件提供 initial 值然后在本地进行控制,则可以继续使用React.memo(因为它仍会呈现对于相同的输入道具来说,是相同的东西),在这种情况下,您不需要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} />; }); onChange发生变化时才会调用。您还可以在那里使用initialValue,以便再次更改useCallback属性时,仅更新onChange上的input,以避免让React删除旧的处理程序并仅在onChange发生更改时设置新的

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} />; }); ,每次调用组件函数时也会创建一个新的handleChange函数。它必须是,因此可以将其作为参数传递到useCallback中。唯一的区别是您使用该新功能还是第一次创建的原始功能(useCallback的结果)。我认为重用为给定的一组依赖关系创建的第一个对象的原因是为了最大程度地减少传递给子组件的更改。

答案 1 :(得分:2)

当道具仅包含onChange而没有其他元素时,在这种情况下不需要useCallback ,因为整个组件已经基于onChange进行了备忘?

否,可能有必要memouseCallback的用途不同。

在没有useCallback的情况下,您可能会对每个渲染器进行“大量计算”:

“大量计算”是指一般情况,而不是仅传递事件值的特定示例。

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} />;
});

如果我们添加一个额外的prop(例如的初始值),那么我认为useCallback会很有用,因为否则handleChange将被重新创建,即使onChange不变,但值已更改。正确吗?

如果您要使用受控组件(由于使用了value的{​​{1}}属性), input将被初始化一次,因此试图记住它是毫无用处的(出于什么目的?):

initialValue

Edit objective-brown-nyuyc