Optimizing React Components: Harnessing the Power of Pure Component and React.memo()

Optimizing React Components: Harnessing the Power of Pure Component and React.memo()

What is a Pure component and pure function?

Based on the concept of purity in programming paradigms, a function is said to be pure if it meets the following two conditions:

  • Its return value is only determined by its input values

  • Its return value is always the same for the same input values

    Example:
    function add(a, b) { return a + b; }

    No matter how many times you call this function with the same numbers, you'll always get the same result. For example:
    console.log(add(2, 3)); // Output: 5
    console.log(add(2, 3)); // Output: 5
    console.log(add(2, 3)); // Output: 5

  • Similarly, in React, a pure component always renders the same output when given the same props and state. For example:

  • Example:

A React component is considered as Pure Component if:

  • It renders the same output for the same state and props.

  • It is a class component and extend the React.PureComponent base class.

  • In above example, No matter how many times you render MyPureComponent with the same value prop, it will always look and behave the same way.

How does a Pure Component work ?

  • Shallow Comparison: The key feature of a Pure Component is that it implements a shouldComponentUpdate() method that performs a shallow comparison of the current props and state with the next props and state.

  • Skipping Renders: If the shallow comparison determines that the props and state haven't changed, the shouldComponentUpdate() method returns false. This tells React not to re-render the component, saving time and resources.

  • Performance Benefits: By skipping unnecessary renders, Pure Components can improve performance, especially in large applications with many components.

Pure components are faster because React checks if they need to update before rendering. It compares props and state with a simple check. If they're the same, React skips the rendering, saving time and resources.

Are React functional components pure?

  1. React functional components are NOT inherently pure in the same sense as PureComponent class components.

  2. Thus, functional components cannot leverage the performance improvements and render optimizations that come with React.PureComponent because, by definition, they are not classes.

To understand why, let's break it down:

  1. Maintenance of state and lifecycle methods:

    • Functional components in React are simply JavaScript functions that accept props as arguments and return React JSX elements. They don't have or manage state or lifecycle methods (STATELESS).

    • PureComponent, on the other hand, is a class component that inherits from React's PureComponent class. It has its own state and lifecycle methods.

  2. State Comparison:

    • PureComponent automatically implements a shallow comparison of props and state to determine if the component needs to re-render. This means that it compares the references of the props and state objects, rather than their contents.

    • Since functional components don't have their own state, they don't perform this comparison. Instead, they rely on React's memoization feature or other optimizations to prevent unnecessary re-renders.

Optimizing Functional Components in React: Preventing Unnecessary Updates.

Unlike class PureComponent, functional components do not have inherent ways to optimize rendering performance. As a result, they may re-render even if the incoming props or state have not changed, leading to potentially unnecessary updates and performance overhead.

However, there are few ways to control, let's dig it

  1. Memoization:

    • Functional components can use React.memo() to memoize their rendering. This means that if the component receives the same props, it will return the same output without re-rendering.
      However, this is not exactly the same as the shallow comparison done by PureComponent.
  2. How React.memo() works?

    • When a component is wrapped in React.memo(), React renders the component and memoizes the result. Before the next render, if the new props are the same, React reuses the memoized result, skipping the next rendering.

      Let's see the memoization in action. The functional component Video is wrapped in React.memo():

      Every time views prop is updated with a new number, VideoViewsRealtime renders. This triggers Video rendering too, even if name and context remain the same.

      This is the appropriate case for applying memoized version of Video component(MemoizedVideo).
      As long as name and context props remain the same (2nd picture), React skips rendering MemoizedVideo. This improves the performance of VideoViewsRealtime component.

    • Second argument in React.memo():

      • Normally, React.memo() compares props using a simple equality check. But sometimes, we might want to customize this comparison to suit our specific needs. That's where the second argument, the arePropsEqual() function, comes in.

When not use React.memo() and Pure Components:

  1. React.Memo():

    • Wrapping a component with React.memo() might not yield performance benefits if:

      • It frequently renders with different props.

      • In such cases, React still compares props on each render, leading to unnecessary processing.

      • This results in more complex code without improving performance, as React performs diffing regardless of prop equality.

  2. Pure Components:

    • When dealing with components that require frequent re-rendering due to dynamic data or frequent updates, the overhead of prop comparison may negate the benefits of using pure components.

Conclusion

React compares the current render with the previous one before updating the DOM. To speed up this process and prevent unnecessary rendering, we use Pure Components for class components and React.memo() for functional components. They both optimize performance by minimizing unnecessary updates.

They're like superheroes swooping in to save the day by memoizing components and props, ensuring smoother rendering and better user experiences.

However, even superheroes have their limits! When your components are prone to frequent prop changes or have complex comparison logic, these optimizations may lose their effectiveness, leaving you with more code complexity and little performance gain. So, while they're great allies in the battle for performance, it's essential to wield them wisely and know when to let them take a backseat.