Search code examples
react-nativereact-hooksmemoizationuse-reducerreact-memo

Prevent Child Rerendering if Parent is Rerendered Using Hooks


My bestSellerDummy data doesn't change, so I'd like to prevent the same Product child to be rerendered if parent rerenders. I have tried using useMemo in parent and React.memo in child but no luck, it's still showing log 'Rendering Product component..' every time parent rerenders. What am I missing here? Please advice.

Note: Parent is expected to be rerendered every time I call addToCart function (of CartContext) in a Product component.

I'm using CartContext, maybe related to this, I'm not sure. Here is the sandbox: 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);

=== EDIT: MY VERSION OF ANSWER ===

Finally! After playing around with useCallback, useMemo, fast-memoize plugin.. What suits the best for me is using useReducer in Context combine with wrapping the expensive component with React.memo. I think this is the most clean and elegant way to optimize child components. Working sandbox is here: https://codesandbox.io/s/eloquent-albattani-8x7h9?file=/src/App.js


Solution

  • Since you are using useContext, your component will always re-renders.

    When the nearest <MyContext.Provider> above the component updates, this Hook will trigger a rerender with the latest context value passed to that MyContext provider. Even if an ancestor uses React.memo or shouldComponentUpdate, a rerender will still happen starting at the component itself using useContext.

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

    I was trying to refactor your code using the 2nd strategy pointed from the docs: https://github.com/facebook/react/issues/15156#issuecomment-474590693.

    However, I soon realized that the addToCart function has cartItems as its dependency, so whenever cartItems changes, addToCart changes and it's kind of impossible to avoid re-renders since every Product component use addToCart function.

    That leads me to the use of useReducer because React guarantees that its dispatch is stable and won't change during re-renders.

    So here's the working Codesandbox: https://codesandbox.io/s/red-feather-dc7x6?file=/src/App.js:786-797