【问题标题】:Prevent Child Rerendering if Parent is Rerendered Using Hooks如果使用 Hooks 渲染父级,则防止子级重新渲染
【发布时间】:2021-02-26 16:29:36
【问题描述】:

我的 bestSellerDummy 数据不会改变,所以我想防止在父级重新渲染时重新渲染相同的 Product 子级。我曾尝试在父级中使用 useMemo 并在子级中使用 React.memo 但没有运气,每次父级重新渲染时它仍然显示日志“渲染产品组件..”。我在这里想念什么?请指教。

注意:每次我在 Product 组件中调用(CartContext 的)addToCart 函数时,都应该重新呈现父级。

我正在使用 CartContext,可能与此有关,我不确定。这里是沙盒:https://codesandbox.io/s/dazzling-moore-po1c6?file=/src/App.js

Home.tsx

const [bestSellerDummy] = useState(
  [...new Array(5)].map((item, key) => ({
    id: key,
    imageUri:'https://1.jpg',
    name: 'My Dummy 1',
    price: 25,
  })),
);

const bestSellers = useMemo(() => {
  return bestSellerDummy.map((productDummy, key) => {
    return (
      <Product key={key} product={productDummy} />
    );
  });
}, [bestSellerDummy]);

return (
  ...
  {bestSellers}
  ...
)

Product.tsx

const Product: FunctionComponent<IProductProps> = (
  productProps,
) => {
  ...
  console.log('Rendering Product component..');
  ...
}

export default React.memo(Product);

=== 编辑:我的答案版本 ===

终于!在玩过useCallbackuseMemofast-memoize 插件之后。最适合我的是在上下文中使用useReducer,并用React.memo 包装昂贵的组件。我认为这是优化子组件最干净优雅的方式。工作沙箱在这里:https://codesandbox.io/s/eloquent-albattani-8x7h9?file=/src/App.js

【问题讨论】:

  • 是什么原因导致父元素重新渲染?您能否通过 Codesandbox 提供一个可重现的最小示例?
  • @dongnhan Parent 预计每次我在 Product 组件中调用 addToCart 函数(属于 CartContext)时都会重新呈现。
  • 你也可以通过第二个回调来比较React.memo中的render的时间。如果您添加沙盒,那就太好了。否则很难从中分辨出来

标签: react-native react-hooks memoization use-reducer react-memo


【解决方案1】:

试试这个方法

const [bestSellerDummy, setBestSellerDummy] = useState([]); // default empty

// get data from `useCallback`
const sellerData = React.useCallback(
  () => {
    return [...new Array(5)].map((item, key) => ({
     id: key,
     imageUri:'https://1.jpg',
     name: 'My Dummy 1',
     price: 25,
  }))

  }, []
);

useEffect( () => {
  setBestSellerDummy( sellerData() ); // set data when screen rendered from `useCallback`
}, [])

const bestSellers = useMemo(() => {
// ....
}, [bestSellerDummy]);
 return (
//  ...
{bestSellers}
//  ...
)

【讨论】:

  • @Jeaf 尝试从 [...new Array(5)] 中删除 ...
【解决方案2】:

也用React.memo 包裹BestSellers 组件。不要使用useMemo 以避免不必要的组件更新,因为它可能会导致错误。它用于计算昂贵的值。

来源:https://reactjs.org/docs/hooks-reference.html#usememo

【讨论】:

  • 我觉得这也是正确的!我结合dongnhan answer作为我的答案版本(在编辑问题中提供)。非常感谢,谢谢。
【解决方案3】:

由于您使用的是useContext,因此您的组件将始终重新渲染。

当组件上方最近的 更新时,此 Hook 将触发重新渲染,并将最新的上下文值传递给该 MyContext 提供程序。即使祖先使用 React.memo 或 shouldComponentUpdate,仍然会使用 useContext 从组件本身开始重新渲染。

参考:https://reactjs.org/docs/hooks-reference.html#usecontext

我试图使用文档中指出的第二个策略重构您的代码:https://github.com/facebook/react/issues/15156#issuecomment-474590693

但是,我很快意识到addToCart 函数将cartItems 作为它的依赖项,所以每当cartItems 发生变化时,addToCart 就会发生变化,而且由于每个Product 组件都使用了它,因此避免重新渲染是不可能的addToCart 函数。

这导致我使用useReducer,因为React guarantees that its dispatch is stable and won't change during re-renders.

这里是工作的 Codesandbox:https://codesandbox.io/s/red-feather-dc7x6?file=/src/App.js:786-797

【讨论】:

【解决方案4】:

这是清除你对 useCallback、useMemo 和 useEffect 概念的最佳方式。

App.js

import Child1 from "./Child1";
import Child2 from "./Child2";
import { useState, useEffect, useMemo, useCallback } from "react";
function App() {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);
  console.log("Parent");
  const printx = useCallback(() => {
    console.log("x:" + x);
  }, [x]);
  useEffect(() => {
    printx();
    console.log("-------");
  }, [printx]);
  const child1 = useMemo(() => {
    return <Child1 x={x} />;
  }, [x]);
  const child2 = useMemo(() => {
    return <Child2 y={y} />;
  }, [y]);
  return (
    <div className="App">
      <h1>Parent</h1>
      <button onClick={() => setX(x + 1)}>X+</button>
      <button onClick={() => setY(y + 1)}>Y+</button>
      {child1}
      {child2}
    </div>
  );
}

export default App;

Child1.js

const Child1 = ({ x }) => {
  console.log("Child1");
  return (
    <div>
      <h1>Child 1:{x}</h1>
    </div>
  );
};
export default Child1;

Child2.js

const Child2 = ({ y }) => {
  console.log("Child2");
  return (
    <div>
      <h1>Child 2:{y}</h1>
    </div>
  );
};
export default Child2;

【讨论】:

    猜你喜欢
    • 2017-03-06
    • 1970-01-01
    • 1970-01-01
    • 2021-09-28
    • 2012-07-16
    • 2018-12-28
    • 2021-11-14
    • 2021-03-12
    • 1970-01-01
    相关资源
    最近更新 更多