Telerik blogs

React Compiler is a new experimental tool designed to automatically enhance the efficiency of React applications by optimizing code execution without requiring significant changes to the existing codebase. See what the React Compiler offers in beta and what it could mean for our React apps.

At React Conf 2024, the introduction of React Compiler marked a pivotal moment for React developers. This experimental tool promises to redefine the optimization of React applications, streamlining performance without necessitating major code rewrites.

In this article, we delve into what the React Compiler offers and how it could reshape our approach to some of the development we do in React applications.

Manual Memoization in React

Before we explore React Compiler, it’s important to first understand the performance challenges that React developers often face. Consider this simple ProductList component:

function ProductList({ products, category }) {
  const [sortOrder, setSortOrder] =
    useState("asc");

  const handleSortChange = (newOrder) => {
    setSortOrder(newOrder);
  };

  const sortedProducts = products.sort(
    (a, b) => {
      return sortOrder === "asc"
        ? a.price - b.price
        : b.price - a.price;
    },
  );

  const filteredProducts =
    sortedProducts.filter(
      (product) =>
        product.category === category,
    );

  return (
    <div>
      <SortControls
        onSortChange={handleSortChange}
      />
      {filteredProducts.map((product) => (
        <ProductItem
          key={product.id}
          product={product}
        />
      ))}
    </div>
  );
}

In the above example, the ProductList component handles product sorting and filtering based on user interactions. As the product list grows or the sorting and filtering operations become more complex, performance issues may arise due to unnecessary re-renders and expensive computations.

Traditionally, developers have relied on manual optimization techniques like React.memo, useMemo and useCallback to address these issues. Here’s how we might optimize the ProductList component:

/* 
  Using React.memo to prevent re-rendering of 
  the component unless `products` or `category` change
*/
const ProductList = React.memo(function ProductList({ products, category }) {
  const [sortOrder, setSortOrder] = useState('asc');

  /* 
    useCallback to memoize the function and 
    prevent it from being recreated on every render
  */
  const handleSortChange = useCallback((newOrder) => {
    setSortOrder(newOrder);
  }, []); // No dependencies here, since `setSortOrder` doesn't change

  /* 
    useMemo to memoize the sorted array of 
    products to avoid costly sorting on every 
    render
  */
  const sortedProducts = useMemo(() => {
    return products.sort((a, b) => {
      return sortOrder === 'asc' ? a.price - b.price : b.price - a.price;
    });
  }, [products, sortOrder]); // Dependencies are `products` and `sortOrder`

  /* 
    useMemo to memoize the filtered array of 
    products to avoid filtering on every render
  */
  const filteredProducts = useMemo(() => {
    return sortedProducts.filter(product => product.category === category);
  }, [sortedProducts, category]); // Dependencies are `sortedProducts` and `category`

  return (
    <div>
      <SortControls onSortChange={handleSortChange} />
      {filteredProducts.map(product => (
        <ProductItem key={product.id} product={product} />
      ))}
    </div>
  );
});

In the above code example, memoization techniques such as React.memo, useCallback and useMemo are utilized to enhance the component’s performance by preventing unnecessary re-renders and optimizing computationally expensive operations.

Memoization is the process of caching the results of expensive function calls and returning the cached result when the same inputs occur again. While effective, this manual approach can be time-consuming and error-prone. Developers must carefully consider which parts of the code to optimize, potentially leading to over-optimization or missed opportunities for performance gains.

Enter React Compiler

React Compiler aims to address these challenges by automating the optimization process. It’s designed to work with existing JavaScript code and understands the Rules of React, eliminating the need for extensive code rewrites.

With React Compiler, we can write idiomatic React code without worrying about manual optimizations. The compiler intelligently applies memoization across components and hooks, preventing unnecessary re-renders and improving overall performance.

Here’s how our ProductList component might look when leveraging React Compiler:

/* 
  Automatically optimized when using React Compiler!
*/
function ProductList({ products, category }) {
  const [sortOrder, setSortOrder] =
    useState("asc");

  /* 
    Automatically optimized when using React Compiler!
  */
  const handleSortChange = (newOrder) => {
    setSortOrder(newOrder);
  };

  /* 
    Automatically optimized when using React Compiler!
  */
  const sortedProducts = products.sort(
    (a, b) => {
      return sortOrder === "asc"
        ? a.price - b.price
        : b.price - a.price;
    },
  );

  /* 
    Automatically optimized when using React Compiler!
  */
  const filteredProducts =
    sortedProducts.filter(
      (product) =>
        product.category === category,
    );

  return (
    <div>
      <SortControls
        onSortChange={handleSortChange}
      />
      {filteredProducts.map((product) => (
        <ProductItem
          key={product.id}
          product={product}
        />
      ))}
    </div>
  );
}

Notice how the above code is identical to our original, unoptimized version. React Compiler takes care of the optimization behind the scenes, allowing developers to focus on writing clean, maintainable code!

Getting Started with React Compiler

While the React Compiler is currently in use in production at Meta, it’s important to emphasize that it’s still in the experimental stage and not yet deemed stable for broad adoption.

For those keen to explore React Compiler before its official release, the React team encourages a few steps to take:

Check Compatibility

Prior to installing the compiler, check the compatibility of your React app with the npx react-compiler-healthcheck@latest command. This script evaluates various aspects of a React project to determine how well it can integrate with the new React Compiler. In particular, the script will:

  • Evaluate the number of components that can be effectively optimized. A higher count indicates better optimization potential.
  • Assess the usage of <StrictMode>. Active usage and adherence suggest a greater likelihood of compliance with the Rules of React.
  • Identify the use of incompatible libraries. The script detects known libraries that do not work well with the compiler.

Install eslint-plugin-react-compiler

Enhance your ability to detect non-compliant code in your React application by installing the eslint-plugin-react-compiler. This ESLint plugin helps pinpoint areas in the code that do not conform to the established Rules of React, revealing which parts might be overlooked by the compiler for optimization until these issues are addressed.

Gradual Rollout

For those updating existing projects, the React team advises a cautious approach to integrating the compiler. Begin by applying the compiler to a limited number of directories and then incrementally extend its use across the entire application. This strategy helps mitigate the risks associated with potential misinterpretations by the compiler, especially in cases where components or hooks may not fully comply with the Rules of React.

Wrap-up

React Compiler represents a significant advancement in how developers can optimize React applications effortlessly. It holds the promise of handling complex memoization and optimization patterns automatically, enabling developers to focus on crafting feature-rich, user-friendly interfaces without the overhead of deep performance tweaking.

For developers excited to adopt React Compiler, it’s helpful to approach its integration with a structured plan:

  1. Initial compatibility checks: Start by confirming your project is a good fit for React Compiler through health checks.
  2. Strategic rollout: Introduce the compiler gradually to specific areas of your project to monitor its impact and effectiveness, adjusting its use as needed based on initial outcomes.
  3. Community engagement and support: Join the React Compiler Working Group to gain additional insights and support as you navigate the early stages of adoption.

In an upcoming article, we’ll explore a tutorial on adding React Compiler to an existing React project. In the meantime, for more comprehensive guides on getting started with the experimental React Compiler, check out the documentation produced by the React team below:

Finally, for more information on using the Compiler, check out these great talks given by the core React team members at React Conf 2024:


About the Author

Hassan Djirdeh

Hassan is a senior frontend engineer and has helped build large production applications at-scale at organizations like Doordash, Instacart and Shopify. Hassan is also a published author and course instructor where he’s helped thousands of students learn in-depth frontend engineering skills like React, Vue, TypeScript, and GraphQL.

Related Posts

Comments

Comments are disabled in preview mode.