【问题标题】:A functional subcomponent doesn't rerender on props change功能子组件不会在道具更改时重新呈现
【发布时间】:2020-06-27 03:15:12
【问题描述】:

我有一个非常重(计算上)的功能组件(父),它没有状态并且具有本地状态的子子组件很少。孩子只依赖于父母发送的道具。

我将一个函数传递给其中一个孩子 (ChildA) 以更改 Parent 上变量的值。

此变量是不同子组件 (ChildB) 的道具之一,该子组件具有基于该道具的状态并在 useEffect 挂钩中更新它。

当作为 prop 传递的值在 Parent 组件上发生更改时,ChildB 组件不会重新渲染。

当然,在 Parent 上引入状态(useState 挂钩)可以解决此问题,但会一遍又一遍地重新渲染父级,并且会破坏性能,因为 Parent 有 500 多个嵌套组件,它们都会重新渲染。

引入某种 Store(Redux、MobX)可能会解决这个问题,但那将是矫枉过正。

一个简化的例子:

import React, { useEffect, useState } from "react";

export default function App() {
  return <Parent />    
}

const ChildA = ({ onAction }) => {
  return <button onClick={onAction}>CLICK</button>;
};

const ChildB = ({ coreValue }) => {
  const [value, setValue] = useState(0);

  useEffect(() => {
    setValue(coreValue);
  }, [coreValue]);

  return <div>My value: {value}</div>;
};

const Parent = () => {
  let calculatedValue = 0;

  const changeValue = () => {
    calculatedValue += Math.random();
  };

  return (
    <div>
      <ChildA onAction={changeValue} />
      <ChildB coreValue={calculatedValue} />
    </div>
  );
};

您可以在这里测试代码:https://codesandbox.io/s/vigilant-wave-r27rg

如何在道具更改时仅重新渲染 ChildB?

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    您必须将值存储在父组件状态中,只需将父组件状态值发送给 ChildB,您不需要维护状态和 useEffect 挂钩来捕获更改。在此处查看代码:https://codesandbox.io/s/adoring-chatelet-sjjfs

    import React, { useState } from "react";
    export default function App() {
      return <Parent />;
    }
    const ChildA = ({ onAction }) => {
      return <button onClick={onAction}>CLICK</button>;
    };
    const ChildB = ({ coreValue }) => {
      return <div>My value: {coreValue}</div>;
    };
    const Parent = () => {
      const [value, setValue] = useState(0);
      const changeValue = () => {
        setValue(value + Math.random());
      };
      return (
        <div>
           <ChildA onAction={changeValue} />
           <ChildB coreValue={value} />
        </div>
      );
    };
    

    【讨论】:

    • 是和否 - 这将解决不重新渲染 ChildB 的问题,但会导致父级重新渲染,我必须避免这种情况,因为这会导致级联的重新渲染超过 500 个组件。我只需要重新渲染 ChildB。
    • 同意!!!但这是您可以使用组件状态的唯一方法。如果您使用的是应用商店(Redux 商店),那么您可以直接从那里更新并使用该值。
    【解决方案2】:

    React 的 useCallback 和 memo 防止不必要的重新渲染。请注意,无论Parent 或ChildB 状态更改的次数如何,ChildA 都不会重新渲染。另外,您当前的示例在ChildB 中不需要useState / useEffect

    https://codesandbox.io/s/usecallback-and-memo-ptkuj

    import React, { useEffect, useState, memo, useCallback } from "react";
    
    export default function App() {
      return <Parent />;
    }
    
    const ChildA = memo(({ onAction }) => {
      console.log("ChildA rendering");
      return <button onClick={onAction}>CLICK</button>;
    });
    
    const ChildB = memo(({ coreValue }) => {
      const [value, setValue] = useState(0);
    
      useEffect(() => {
        setValue(coreValue);
      }, [coreValue]);
    
      return <div>My value: {value}</div>;
    });
    
    const Parent = () => {
      const [calculatedValue, setCalculatedValue] = useState(0);
    
      const changeValue = useCallback(() => {
        setCalculatedValue(c => (c += Math.random()));
      }, []);
    
      return (
        <div>
          <ChildA onAction={changeValue} />
          <ChildB coreValue={calculatedValue} />
        </div>
      );
    };
    

    【讨论】:

    • 嗯,这可能有效,但记住某人的子组件会很棘手。我想我会在项目中添加一个商店以避免所有这些麻烦。感谢您的建议。
    猜你喜欢
    • 2023-02-15
    • 1970-01-01
    • 2021-08-27
    • 2020-05-12
    • 2021-06-15
    • 2021-12-22
    • 2018-08-09
    • 1970-01-01
    • 2021-01-26
    相关资源
    最近更新 更多