DEV Community

Cover image for Optimizing React Apps with React 18 and Below ⚛️⚡
Abdullah Zafar
Abdullah Zafar

Posted on

Optimizing React Apps with React 18 and Below ⚛️⚡

Table of Contents

  1. Introduction
  2. Reasons for Optimization and Solutions
  3. Understanding Component Re-Rendering
  4. Optimization Techniques and Tools
    • Passing components as Children
    • Understanding memo()
    • Hooks for Different Use Cases
  5. Deep Dive into Optimization Hooks
    • The memo() Function
    • JavaScript's Object Identity
    • useMemo and useCallback
  6. Trade-Offs in Over-Optimizing
  7. What's New in React 19
  8. Conclusion

Introduction 👋

As far as your React app’s performance is concerned, React offers several optimizing tools and mechanisms to run your apps much more smoothly and responsively. Before deep diving into those tools, we need to figure out the reasons those techniques are being deployed into production code.

  • To prevent wasted renders
  • To improve overall application speed, improve responsiveness, and overall user experience.
  • Reducing bundle size for faster production handling.

Reasons for Optimization and Solutions 🤔🚀

  • To Prevent wasted renders: To prevent wasted renders, we usually use

    • memo()
    • useMemo hook
    • useCallback hook
    • Passing elements as a children or regular props
  • To improve overall application speed, improve responsiveness, and overall user experience. They’re are options including

    • useMemo hook
    • useCallback hook
    • useTransition hook
  • To reduce bundle size, ensure our app is much lighter to handle by the server when pushed to production. It typically consists of

    • Using fewer 3rd-party packages
    • Code splitting and lazy loading.

Understanding Component Re-Rendering 📦

A component instance is re-rendered in three different situations namely

  • State changes.
  • Context changes
  • Parent re-renders (⛔ As of notice, the props don’t causes the child component to re-render, it just creates a false impression that it re-renders the component. It is the parent component that re-renders the child component)

🧩 Component Rendering: Rendering a component doesn’t mean updating the DOM; rather, it means the component function gets called every time state or parent linked to it changes.

🗑️ Wasted Re-Rendering: When a component renders twice or more, without showing any change in the result, it is what we call a wasted render yeah, for small applications this is fine but for big and enterprise-level apps, this can cause a huge performance issue for the end user and cause backfire on the enterprise’s reputation.

🕵 Monitoring Re-Renders: Luckily, React Developer Tools, if you didn’t hear about them, are like chrome developer tools but for React applications development and it primarily consists of two tabs of notice, Components tab and Profiler tab. The components tab is used for displaying the component tree whereas the Profiler Tab, which is of more importance in case of monitoring optimization, can record the render changes and thus log those changes and their time elapsed on a chart to monitor the render time of each component linked to changes. Just make sure to check the “Record why each component rendered while profiling” option in the Profile Tab > settings > Profiler section if you have the React Dev Tool Chrome extension installed.

Optimization Techniques and Tools 🔧🔎

1. Passing big functional components as children

When props are sent as children props from the main component to the child component like:

// Child component
const ChildComponent = () => {
  return <h2>This is the Child Component!</h2>;
};

// Parent component
const ParentComponent = ({ children }) => {
  return (
    <div>
      <h1>Parent Component</h1>
      {children} {/* Render the passed function component here */}
    </div>
  );
};

const App = () => {
  return (
    <ParentComponent>
      <ChildComponent />
    </ParentComponent>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

In this case, the children component has already been created before the component re-renders. The children prop thus get no change when state changes in the parent component if it doesn't need a re-render unless they also want that state update themselves.

2. Understanding memo() Function

  • 🧠 Memoization: The term came from the latin word memorandum which means "to be remembered". In programming, memoization is an optimization technique that makes applications more efficient and hence faster.
  • Memo prevents re-rendering unless props change.
  • If the same function is called with the same arguments, the saved result in the cache is returned without executing the function twice.
  • Its general syntax goes by this:
   import {memo} from "react"

    function App() {
        return (
            <TestComp />
        )
    }

   const TestComp = memo(() => {
       return (
           <div>This is the memoized test functional component</div>
       )
   })
Enter fullscreen mode Exit fullscreen mode

graph-1

graph-2

3. Hooks for Different Use Cases

A typical React app consists of three major components or items, and for each item, there's a unique hook to optimize those, namely:

  • For component, memo()
  • For JS objects, useMemo()
  • For functions, useCallback()

Deep Dive into Optimization Hooks 🤿🪝

1. The memo() Function

   import {memo, useState} from "react"

    function App() {
        const [text, someText] = useState("")
        return (
            // Since the TestComp is memoized unless the state changes
            // it will not re-render even when the parent re-renders
            // as long as the text prop remains the same.
            <TestComp text={text} />
        )
    }

   const TestComp = memo(({text}) => {
       return (
           <div>{text}</div>
       )
   })
Enter fullscreen mode Exit fullscreen mode

When a component is memoized, using the memo() function, it is stored in the cache unless the passed prop is changed, causing it to re-render.

graph-3

A memoized component will still re-render if its own state or context updates within its parenthesis. With all of its advantages kept in mind, it shouldn't be used quite often as it contains tradeoffs too. For instance, memoizing too many components may slow down the user's computer as the value stored is placed in the cache and it can clog over time, thus causing lag in the React app reducing user experience. Indeed, only those components that have huge sides that are tightly coupled together should be memoized to improve overall performance.

2. JavaScript's Object Identity

As we all know, JS was created within 10 days by Brendan Eich so there are many weird stuff going around which even confuses developers in the present age.
So take a look at this figure

graph-4

3. useMemo() and useCallback() Functions

For the solution of object and function that are re-renders even with memo function, two hooks are provided by React team to persist their values as necessary, namely useMemo and useCallback hooks.
Both hooks are used to seperate:

  • Memoize objects with useMemo
  • Memoize functions with useCallback

Note like the useEffect hook, both useMemo and useCallback take a dependency array that if it is changed, the value present in these two optimization hooks will re-created.

graph-5

Just like use cases of these two hooks, their syntaxes are also different. Key takeaways are:

  • useMemo takes a callback function, which can be an arrow function, which will call the initial render and store the value in cached memory.
  • useCallback will not instantly call function body but rather memoize the function.
  • useMemo simply memoize the result of calling the callback vs useCallback will memoize the function.

Trade-Offs in Over-Optimizing 🏷

By going through this whole blog, many would think, yeah let's just memoize each every component we humanly can to make out apps even more smoother. ⭕NOT EXACTLY! See, memoize came from latin word memorandum which means "to be remembered". And in Computer Science, it sounds the same. Computer takes "to be remembered" too seriously and stores the memoized result in its own memory and yeah surely for smaller projects it means nothing, but as a programmer, we don't want to make small apps rather quite big and complex apps, and getting used to this bad habit of over-optimizing will do more bad than good for bigger applications like enterprise level. Not really the result we're loooking, right? So by following best practices, we only want to memoize those components, functions and objects, you get it, who are creating causing our app to slow down to causing other components to slowly re-render although those are not big components. Remember, these sense of good optimization techniques will not come overnight and one has to make his hands dirty to experience and learn those.

What's new with React 19 and what it has to do with optimization ⚛</>

Have you ever noticed, why the other frameworks like Angular and Svelte are far more optimized than React, well they have their own compiler integrated. Okay, so if they have their own compiler which is making their optimization far more easier and mostly handled by compiler itself, so should the React have their own compiler, right? Well that is the biggest announcement for the React 19 and above. React is finally getting is own compiler. The React team announced it with a lot more interesting new features including:

  • React Compiler 🤖
  • Server Components 🌐
  • Assets Loading ⏳
  • Enhanced Hooks 🪝

You can further read about other details at React 19 RC.
So, the React compiler has been announced at it was the most eye-capturing news of 2024 📰. After the launch of React compiler, the developers no longer need to think about small details of optimizing their apps and now is in charge for their app to run as smoother as possible. It will surely save a lot of time for professional developers delievery high-end products and beginner level programmer, by making the learning curve much more smoother as they no longer need to spend a lot of time thinking about best practices and calculating wether the component should be memoized or not.

Conclusion 🔚

React provides powerful tools to enhance app performance, but effective optimization requires understanding when and where to apply these techniques. While memoization can prevent wasted renders and improve speed, overuse can lead to its own challenges. With React 19 introducing its own compiler, optimization will become more automated, freeing developers to focus on building robust and efficient apps.

Top comments (3)

Collapse
 
osajah_raza_8ad8c1de59ba7 profile image
Osajah Raza

Great article on optimizing React apps! The tips on memoization and lazy loading were particularly insightful. It's amazing how small changes can significantly boost performance. I'm looking forward to implementing these strategies in my projects. Thanks for sharing such practical advice!

Collapse
 
osajah_raza_8ad8c1de59ba7 profile image
Osajah Raza

Crazyyy blog

Collapse
 
muhammad_ahmad_09aaff1975 profile image
Muhammad Ahmad

Intresting!