How did I re-render: Performance and Best Practices for React Components

Bhavya Saggi
4 min readSep 25, 2021

--

React achieves its titular ‘reactivity’ through a hierarchical structure of Components, each working in seclusion and communicating with each other through props & managing internal state.

React verifies a change via shallow-compare check, and decides that a Component needs to re-render when:

  • The parent Component re-renders.
  • State value of Component changes.

By stabilizing the aforementioned, it can be ensured that no unnecessary re-renders occur and the user experience stays smooth.

This also means ensuring the best usage of React APIs and avoiding Anti-Patterns. Following is a compilation of various paradigms that could be followed to improve the quality of code, performance, and user experience.

Pure Components & Prop References.

An idempotent function, given the same input, will always produce the same output and has no side effects. Similarly, a Pure Component returns the same output, if its props, state, and context remain unchanged.

Benefits of a Pure Component include predictable output, lower re-renders, and easier debugging. Keeping the Components (& Hooks) Pure requires the following guidelines to be met:

  1. A Pure Component should maintain the referential integrity of the props that are passed to the Child Components. That means, for the same set of values received by a Pure Component, the props of the Pure Component’s child components should stay the same.
    This implies reduced usage of complex objects or arrow functions as inline props.
  2. A Pure Component should not create side effects. That requires no modification of the application’s state or external dependencies.
    This implies reduced usage of useEffect hook.

Read more about a few implied guidelines for React code on: https://gist.github.com/sebmarkbage/75f0838967cd003cd7f9ab938eb1958f

If the prop value is complex, then the prop could be memoized, by either using useMemo or useCallback hook. But, it is to be noted that excessive use of hooks may cause a performance dip by adding an extra layer of initial computation and memory reference.

You may also pass entire Component as a prop, to prevent re-rendering. Read more on https://kentcdodds.com/blog/optimize-react-re-renders

But, inline objects or functions don’t necessarily lead to a performance boost. They are easy to read, write, and organize, especially when the definitions are short and simple.

State Colocation & Single Source of Truth

Re-rendering the entire Parent Component is unnecessary when only a single Child Component reflects a change based on a state variable.
Similarly, re-rendering of a Child Component is unnecessary because of a state change in Parent Component.

If a state variable is not used by a React Component, then the Component should not re-render if that state value changes.

Therefore, it is desirable to move a state variable as deep as possible in a React Component hierarchy, to reduce props passed by the Parent Component. This technique has been named `State Colocation`, and dictates the following points:

  1. Separate state into different logical pieces rather than in one big store.
  2. Push logic down to the Child Component, as deep as possible.

Read more on: https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster

Also, if two or more Child Components share a state value, it is best to keep the state in the Least-Common-Ancestor Component and pass it down as props. This is known as maintaining a “Single Source of Truth”, as only a single Ancestor Component is responsible for maintaining & updating the state.

Read more on: https://reactjs.org/docs/lifting-state-up.html

Conclusion

In this blog post, we observed that

  1. Avoiding inline objects or functions as props to Child Components reduces the chance of triggering a re-render. As inline declarations create new references even if the underlying value is unchanged while generating memory and garbage collection concerns.
  2. React is a state management library and its advancement brings a lot to the table, which if used correctly mitigates the need for additional dependencies.
  3. Effective use of state and maintaining Component hierarchy is at the core of writing “good code”. Always place the state variable where is used, and don’t be afraid of passing Components as children or via props.

A common rule of thumb is to write the code first, then measure, and only optimize if needed. Strictly following paradigms can lead to micro-optimizations, having only a slight gain over low code readability and inefficient code organization.

--

--