What do we know about React Forget | Code Insight
What do we know about React Forget
For those unfamiliar, the React Core Team has been cooking up something exciting called React Forget. Insights from recent events, such as React Advanced London 2023 and React India 2023, were shared by active members of the React Core Team—Joe Savona, Mofei Zhang, and Sathya Gunasekaran. These talks shed light on the challenges, tricks, insights and future roadmap of React Forget.
Sathya, at React India, delved into the challenges of creating a compiler, showcased some tricky cases, and explained why the React model is perfect for compiler implementation. Meanwhile, at React Advanced London, Joe Savona and Mofei Zhang demonstrated how the compiler optimizes code on a Playground. They also discussed a use case involving the compiler at Facebook and insights into React Forget.
What is React Forget?
In essence, React Forget is a compiler designed to analyze your React code and automatically memoize your Components and Hooks. The goal? To eliminate unnecessary rerenders and boost performance.
Why React Forget?
React operates on a unidirectional data flow. When a component rerenders, by default, all the components returned by that component will rerender as well. While this default behavior is usually fast, in some cases, especially with numerous components or a slow-to-render component, performance can suffer.
React provides APIs like React.memo
, useMemo
, and useCallback
to memoize components and props, preventing unnecessary rerenders. However, their usage is a hot topic, with debates about memoizing everything versus avoiding memoization altogether. Striking a balance between performance improvements and code readability is the key to manual memoization.
While performance matters, React developers should focus on the UI rather than memoization. Pre-optimizing React code wrapping everything on React.memo
, useMemo
, and useCallback
can boost performance but harm developer experience (DX), making the code hard to read and maintain.
Let the compiler analyze your code and implement optimizations during the build phase, allowing developers to concentrate on the UI and write cleaner code. React Forget simplifies React development and optimizes apps by default.
Who is Behind React Forget?
The project is currently in development at Meta, and not yet open-sourced.
When Can We Expect React Forget?
Unfortunately, React Forget is not yet production-ready and available to the public. As Mofei Zhang explained, Meta maintains high standards before releasing a product into production. Nevertheless, React Forget is already being used and tested at Meta in real apps like the Quest Store product details page and Instagram profile page for the web.
After further testing and analysis, React Forget will be deployed on other Meta products and, once it passes all tests, released to the public and open-sourced.
Curious about a Meta App using React Forget? Inspect the code and look for the unstable_useMemoCache hook. If it's there, the compiler worked its magic! 😁
Where Does React Forget Operate?
Given that the compiler's goal is to avoid unnecessary rerenders, React Forget currently optimizes code on client components when they are running on the client. Future versions might extend optimizations to server-side rendering (SSR).
Compilation occurs during the build phase, and the optimized code is outputted in the bundle. To be crystal clear, React Forget won't execute during runtime.
How
Thanks to the principles of immutability, purity, and React's component-driven design, the compiler's job is streamlined. It doesn't need to analyze your entire codebase; optimizations happen at the component level and can be parallelized.
In React, the UI is a function of the state. For a given state, a React component should always return the same UI. So, if the props and state didn't change, the component doesn't need to rerender.
React Forget memoizes all components and hooks. It's as if the compiler is wrapping all components in React.memo
, functions in useCallback
, and non-primitive values in useMemo
. In reality, the compiler isn't using these React APIs; it's employing low-level optimizations that function similarly, but even more efficiently.
Compilers excel at optimization, often surpassing handcrafted code. As shown by the React Core team, imagine a component like this:
The problem here is that every time the title changes, both <Heading />
and <VideoList />
will rerender. While <Heading />
should rerender because it depends on title
, <VideoList />
doesn't—it depends only on filteredVideos
.
One might think, "Let's wrap <VideoList />
in React.memo
" but that doesn't solve the issue because React.memo
compares props using Object.is()
, and in JavaScript, [] !== []
(non-primitives are compared by reference, not value), so filteredVideos
will always be different.
To make this work, we need to wrap filteredVideos
in a useMemo
.
As mentioned, the compiler doesn't use these APIs and would produce code like this:
First tests
The initial tests are bringing good news. Even though the Quest Store product details page was manually optimized by Meta engineers, the compiler outperformed human optimization.
As demonstrated by Mofei Zhang, the compiler did a better job than Meta engineers at avoiding rerenders on the Quest Store product details page. The compiled version skipped the rerender of all components marked with the red rectangle, resulting in a page load improvement of 4-12% and a tab change 150% faster.
Original code:
Compiled version: