如何防止重新渲染未更改的组件?

IT技术 javascript reactjs react-hooks
2021-04-21 05:21:55

我有一个由几个其他组件(例如文本字段)组成的组件,当对文本字段进行输入时,所有其他组件都会重新呈现。我想防止重新渲染,只重新渲染实际更改的组件。我已经看到“useCallback”是执行此操作的正确方法,并且我已经看到了如何使用它。但是,我在让“useCallBack”为我的案例正常工作时遇到了一些麻烦。即使我以如下简单的方式进行设置,文本字段中输入的每个新字符都会导致按钮再次呈现。我没有看到我的错误。

请参阅沙箱中的工作示例。

const Button = () => {
  console.log("Button Rendered!");
  window.alert("Button Rendered");
  return <button onClick="">Press me</button>;
};

export default function App() {
  const [textInput, setTextInput] = useState("Hallo");

  const onChangeInput = useCallback(
    (e) => {
      setTextInput(e.target.value);
    },
    [textInput]
  );

  return (
    <div>
      <input
        type="text"
        onChange={onChangeInput}
        value={textInput}
      />
      <Button />
    </div>
  );
}

我很高兴任何校准。

3个回答

我个人会避免React.memo/ React.useRef/ React.useCallback

您的示例最简单的解决方案是创建另一个组件,并用它存储状态。

例如。

const Button = () => {
  console.log("Button Rendered!");
  window.alert("Button Rendered");
  return <button onClick="">Press me</button>;
};

const TextInput = () => {
  const [textInput, setTextInput] = useState("Hallo");
  const onChangeInput = useCallback(
    (e) => {
      setTextInput(e.target.value);
    },
    [textInput]
  );
  return (
      <input
        type="text"
        onChange={onChangeInput}
        value={textInput}
      />
  );
}


export default function App() {
  return (
    <div>
      <TextInput/>
      <Button />
    </div>
  );
}

在上面如果你改变了 Text,App 中没有 State 改变,所以 Button 不会被重新渲染,不需要 useMemo 等。

你会发现 React 工作得非常好,你将组件划分得越多,它不仅解决了重新渲染的问题,而且可能使以后重用组件变得容易得多。

IOW:保持状态尽可能靠近组件,性能将随之而来。

当然,您的示例很简单,在实际应用程序中,您将需要处理 HOC 等,但这是另一个问题.. :)

@dan_boy 是的,这就是我提到的关于 HOC 的部分。同样,原则是相同的,将状态保持在需要的地方。在 App 中放置一个 setState 将导致重新渲染它的所有子项。为了防止这种情况,大多数人对 React 所做的是使用更健壮的状态管理,Redux 是一个流行的选择。但这不是唯一的选择,我个人使用基于代理的我自己的。在此发布另一个问题可能是一个想法。
2021-06-12 05:21:55
谢谢你的回复,它给了我我需要的理解,尤其是关于备忘录的缺点。但是,我现在想知道如何防止在包含多个子组件的组件中重新渲染。请参阅此处在 SandBox 中修改后的示例。在示例中,一旦父组件将输入保存在状态中,按钮就会再次重新渲染。[沙盒](codesandbox.io/s/solitary-smoke-8uzh3?file=/src/App.js
2021-06-18 05:21:55

useCallback不会阻止重新渲染。React.memo是什么防止渲染。它对之前的 props 和新的 props 做一个浅比较,如果它们相同,它会跳过渲染:

const Button = React.memo(() => {
  console.log("Button Rendered!");
  window.alert("Button Rendered");
  return <button onClick="">Press me</button>;
});

useCallback 在这方面的唯一作用是,有时您希望将函数作为props传递给记忆化组件。为了让 memoization 起作用,props 需要保持不变,而 useCallback 可以帮助 props 保持不变。

更改状态会导致重新渲染组件及其所有继承人,为了防止重新渲染某些部分,您可以使用 useMemo 来防止不必要的重新渲染...

注意:useMemo 有一些成本......所以不要过度使用它(在这个小例子中,根本不推荐使用)。

在这种情况下,如果您不需要重新渲染,您可以使用 useRef 来保存输入引用以在需要时获取该值。

例如:

const BlahBlah = () => {
  const inputRef = React.useRef(undefined);

  return (
    <div>
        <input ref={inputRef} />
        <button onClick={() => console.log(inputRef.current.value)}
    </div>
  );
};