DEV Community

Manoj Kumar Patra
Manoj Kumar Patra

Posted on

1

Using React.memo

# Definition

React.memo

  • A higher order component that can be used to get some performance boost if a component renders the same results given the same props
  • Checks for prop changes, and skips rendering the component, and reuse the last rendered result in case previous and current props are the same

function PizzaComponent({ name, price }) {
  /* render using props */
  return (
    <div>Pizza: {name}</div>
    <div>Total: ${price}</div>
  )
}

export default React.memo(PizzaComponent);

Enter fullscreen mode Exit fullscreen mode

This component is a candidate for memoization as given a 🍕 image, it always renders the same 🍕 details.

  • By default, it does a shallow comparison of props
  • For custom control on props comparison, a custom comparison function can be provided as a second argument.

Let's extend our PizzaComponent to also show different toppings available for a 🍕. Toppings will be an array of strings. As mentioned above, React.memo does a shallow comparison of props, so, in this case, even though the toppings array remains same, it will still re-render.

So, how do we fix this?

We pass a custom comparison function as follows:


function PizzaComponent({ name, price, toppings }) {
  function areEqual(prevProps, nextProps) {
    /*
    return true if passing nextProps to render would return
    the same result as passing prevProps to render,
    otherwise return false
    */
    return (
      prevProps.name === nextProps.name,
      prevProps.price === nextProps.price,
      prevProps.toppings.every(topping => nextProps.toppings.includes(topping))
    )
  }

  /* render using props */
  return (
    <div>Pizza: {name}</div>
    <div>Total: ${price}</div>
    {toppings.map((topping, index) => (
      <div key={`${name}_${topping}`}>{topping}</div>
    ))}
  )
}

export default React.memo(PizzaComponent, areEqual);

Enter fullscreen mode Exit fullscreen mode

# Using React memo

Use React.memo to get a performance boost when:

  1. Component re-renders often
  2. Component is usually provided with the same props during re-rendering
  3. Component contains a relevant amount of elements to reason props equality check

If a component with hooks is wrapped with React.memo, then it will still re-render when state or context changes.

# React memo with function as props

Let's add the functionality to order a 🍕 in our PizzaComponent called onOrder as follows:


function PizzaComponent({ name, price, toppings, onOrder }) {
  function areEqual(prevProps, nextProps) {
    /*
    return true if passing nextProps to render would return
    the same result as passing prevProps to render,
    otherwise return false
    */
    return (
      prevProps.name === nextProps.name,
      prevProps.price === nextProps.price,
      prevProps.toppings.every(topping => nextProps.toppings.includes(topping))
    )
  }

  /* render using props */
  return (
    <div>Pizza: {name}</div>
    <div>Total: ${price}</div>
    {toppings.map((topping, index) => (
      <div key={`${name}_${topping}`}>{topping}</div>
    ))}
    <button onClick={onOrder}>Order</button>
  )
}

export default React.memo(PizzaComponent, areEqual);

Enter fullscreen mode Exit fullscreen mode

and let's use it as follows:


function App ({ store }) {
  const { pizza } = store;
  return (
    <PizzaComponent
      name={pizza.name}
      price={pizza.price}
      toppings={pizza.toppings}
      onOrder={() => placeOrder(pizza.price, pizza.coupon)}
    />
  )
}

Enter fullscreen mode Exit fullscreen mode

Since, React.memo does a shallow comparison, this will do a re-render everytime as it will see the callback function onOrder as a new prop everytime.

To fix this, we can wrap the function passed to onOrder with useCallback as follows:


function App ({ store }) {
  const { pizza } = store;
  /**
   * This will always return the same function instance as long as pizza is the same.
   */
  const onOrder = useCallback(
    () => placeOrder(pizza.price, pizza.coupon),
    [pizza],
  );

  return (
    <PizzaComponent
      name={pizza.name}
      price={pizza.price}
      toppings={pizza.toppings}
      onOrder={onOrder}
    />
  )
}

Enter fullscreen mode Exit fullscreen mode

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay