Made in Builder.io

Upcoming webinar with Figma: Design to Code in 80% Less Time

Announcing Visual Copilot - Figma to production in half the time

Builder.io logo
Talk to Us
Platform
Developers
Talk to Us

Blog

Home

Resources

Blog

Forum

Github

Login

Signup

×

Visual CMS

Drag-and-drop visual editor and headless CMS for any tech stack

Theme Studio for Shopify

Build and optimize your Shopify-hosted storefront, no coding required

Resources

Blog

Get StartedLogin

‹ Back to blog

Web development

React’s Upcoming Compiler Only Solves Part Of The Problem

March 2, 2023

Written By Miško Hevery

I spoke about how I think signals are the future of web frameworks. Since then, I am excited to welcome Angular to the Signals club.

What took me by surprise is this tweet where Andrew Clark wrote that he thinks that React Forget is a better approach than Signals.

React Optimizing Compiler aka React Forget

React Forget is a labs project that aims to improve the rendering performance of React applications by automatically generating the equivalent of useMemo and useCallback calls to minimize the cost of re-rendering while retaining React’s programming model.

Signals don’t need memoization for the most part. React Forget automatically inserts memoization everywhere, so I can see how one could come out with the impression that memoization is the same as signals.

Let me show you where the memoization approach fails and Signals shine.

Let’s build a classic single-page application containing a buy button and a shopping cart. This example was chosen because it demonstrates a real-world scenario where state, mutation, and rendering are separated into distinct components:

  1. Where the state is declared (shopping cart content)
  2. Where the state is mutated (the buy button)
  3. Where the state is rendered (the shopping cart UI)
Diagram of an application render tree.

The key thing to understand is that we need to get the state (and the setter) down to the components that need it.

In our case, it is the ShoppingCart and BuyButton components. That means we must prop-drill our way through many layers of components to get there (context is an alternative way to do this, but the result is the same).

Diagram of an application render tree highlighting prop drilling.

Now let’s look at what happens when we try to mutate the shopping cart:

  1. The BuyButton has the setCart setter, which is invoked with the new state of the cart.
  2. The invocation of setCart invalidates the App component.
  3. React starts re-rendering starting at the App component. Usually, this would result in most child components re-rendering, but let’s assume React Forget has turned memoization to the max. In that case, React still needs to render all components between App and ShoppingCart. In our simple case, it is just Header and User, but it is many more in a real-world app.
Diagram of an application render tree. highlighting header component render.

Even though React Forget compiler memoizes everything, it still could not avoid the fact that when the state mutates, it has to be prop-drilled from the state storage all the way down to the place where it is needed.

Re-rendering intermediate components that are used only for prop drilling is wasteful.

Now let’s look at signals. The data-flow diagram looks identical as before, except that instead of passing state, we are now passing signals through many layers of components to ShopingCart and BuyButton.

Diagram of an application render tree highlighting prop drilling.

Now let’s look at what happens when we try to mutate the shopping cart.

  1. The BuyButton has a setCart signal, which it invokes with the new state of the cart.
  2. The signal notifies the ShoppingCart to update the state of the cart.

That is it.

Diagram of an application render tree. highlighting only shopping cart component render.

Notice that even though the state is declared in the App component, the App is not part of the re-rendering process, and neither are all of the components which were part of the prop-drilling of the state to pass it to the ShoppingCart.

That is where the power of signals lies.

This is why we say that, typically, Signals don't need useMemo(). Out-of-the-box, Signals enable you to perform surgical updates of the UI only, which require updating. Signals allow you to skip all of the intermediate parts.

useMemo() can’t skip component layers. At best, useMemo() can prune the component tree so there are fewer branches to visit, but updates still require that we descend through these branches.

Signals and memoization may be equivalent, but in reality, signals are a lot more efficient because they are not bound by the component render tree.

They exist on a plane independent from the component tree.

Indeed, Signals do not require memoization, but memoization in React can’t match the surgical nature of signals.

Introducing Visual Copilot: convert Figma designs to code using your existing components in a single click.

Try Visual Copilot

Share

Twitter
LinkedIn
Facebook
Hand written text that says "A drag and drop headless CMS?"

Introducing Visual Copilot:

A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot
Newsletter

Like our content?

Join Our Newsletter

Continue Reading
AI9 MIN
How to Build AI Products That Don’t Flop
WRITTEN BYSteve Sewell
April 18, 2024
Web Development13 MIN
Convert Figma to Code with AI
WRITTEN BYVishwas Gopinath
April 18, 2024
Web Development8 MIN
Server-only Code in Next.js App Router
WRITTEN BYVishwas Gopinath
April 3, 2024