Adil Haddaoui

/ Home/ Uses

React Redefined: Unleashing it's Power with a new Compiler

Cover Image for React Redefined: Unleashing it's Power with a new Compiler
Photo of Adil Haddaoui
Adil Haddaoui

A few weeks ago, the ReactJS team unveiled a shocking blog post update that has the react community buzzing. As a platform known for its dynamic capabilities and widespread adoption, React has been both celebrated and critiqued. Critics, myself included, have often pointed out its unfixed complexity when handling states recomputing on re-renders via manually handling memoization compared to other frameworks like Svelte and Solid etc ..., and this is possible for those frameworks because unlike React which is purely runtime based they've a compiler that allows you to write more simplified code , However, the latest announcement from the React team addresses many of these concerns head-on, introducing a compiler that promises to dramatically improve the developer experience and get rid of many unnecessary hooks.

The Compiler: React's New Backbone

The introduction of a compiler to React is a game-changer. This move is not just an incremental update; it's a foundational shift that will redefine how developers interact with React. Meta has been using this compiler in production for Instagram, showcasing its potential to not only improve performance but also to simplify the codebase significantly.

Before and After: A Code Comparison

To understand the impact of this update, let's look at how code changes before and after the introduction of the compiler.

Before: The Use of useMemo
1import React, { useState, useMemo } from 'react';
2
3function ExampleComponent() {
4 const [count, setCount] = useState(0);
5 // This value will be unnecessarjy recomputed every time the component re-renders
6 // const halfCount = count / 2
7
8 // to fix the above we had to memoize the count
9 const halfCount = useMemo(() => {
10 return count / 2;
11 }, [count]);
12
13 return (
14 <div>
15 <p>Half the count: {halfCount}</p>
16 <button onClick={() => setCount(count + 1)}>Increment</button>
17 </div>
18 );
19}
After: Simplified State Computation

With the compiler optimizing behind the scenes, explicit memoization becomes unnecessary for many cases.

1import React, { useState } from 'react';
2
3function ExampleComponent() {
4 const [count, setCount] = useState(0);
5 const halfCount = count / 2; // Automatically optimized
6
7 return (
8 <div>
9 <p>Half the count: {halfCount}</p>
10 <button onClick={() => setCount(count + 1)}>Increment</button>
11 </div>
12 );
13}
Before: Forwarding Refs with forwardRef
1import React, { forwardRef } from 'react';
2
3const FancyButton = forwardRef((props, ref) => (
4 <button ref={ref} className="FancyButton">
5 {props.children}
6 </button>
7));
After: Direct Refs as Props

The update allows for a more straightforward use of refs, reducing boilerplate code.

1import React from 'react';
2
3const FancyButton = ({ ref, children }) => (
4 <button ref={ref} className="FancyButton">
5 {children}
6 </button>
7);

Enhanced Promises Handling

The use hook is another significant addition, that simplifies the interaction with asynchronous data by enabling the direct use of promise values within the component's UI. This is a departure from the traditional use of useContext and useEffect hooks to manage asynchronous operations, which often resulted in more boilerplate code.

1import React, { Suspense } from 'react';
2
3const UserProfile = () => {
4 const user = use(fetchUser('userId')); // Simplified async handling
5
6 return (
7 <div>
8 <p>Name: {user.name}</p>
9 <p>Email: {user.email}</p>
10 </div>
11 );
12};

Example: Fetching User Data with use()

Consider the scenario where we need to fetch user data and display it within our component. The use() hook allows us to do this more succinctly, handling both the promise state and its resolution directly:

1import React, { Suspense, useState, ErrorBoundary } from 'react';
2
3function fetchUserData(userId) {
4 return fetch(`https://api.example.com/users/${userId}`)
5 .then(res => res.json());
6}
7
8function UserProfile({ userId }) {
9 const user = use(fetchUserData(userId)); // Use the `use()` hook for promises
10
11 // enables the direct integration of resolved promise values into the user interface
12 return (
13 <div>
14 <h1>User Profile</h1>
15 <p>Name: {user.name}</p>
16 <p>Email: {user.email}</p>
17 </div>
18 );
19}
20
21function App() {
22 const [userId, setUserId] = useState(1);
23
24 return (
25 <ErrorBoundary fallback={<p>Oops! Something went wrong.</p>}>
26 <Suspense fallback={<p>Loading...</p>}>
27 <UserProfile userId={userId} />
28 </Suspense>
29 </ErrorBoundary>
30 );
31}

In this example, Suspense handles the loading state, displaying a loading message until the promise resolves. The ErrorBoundary component, a pattern for catching JavaScript errors in a component tree, handles any potential errors from the promise, such as network errors, by displaying a fallback UI.
While I'm not the biggest fan of this code myself, it certainly better that resolving a promise using the useEffect hook.

Advantages of Using the use() Hook

  • Simplification: It reduces the complexity of handling asynchronous data, making code cleaner and easier to understand.

  • Integrated Error Handling: Combined with Error Boundaries, it provides a streamlined way to handle errors from asynchronous operations.

  • Flexibility: It can be used with promises and React context, offering a unified approach to managing dynamic data and context state.

The introduction of the use() hook, alongside React's compiler improvements, marks a significant evolution in the framework's capabilities, making it more powerful and developer-friendly. These changes address some of the long-standing criticisms of React, particularly around the verbosity and complexity of handling asynchronous data and state management.

The Future of React and Web Development

The React team's latest update is a testament to their commitment to innovation and improving developer experience. By addressing key areas of complexity and inefficiency, React not only sets a new standard for itself but also challenges other frameworks to elevate their game.

As the lines between frameworks become increasingly blurred, the question arises: are we moving towards a unified approach to web development? While diversity in tools and frameworks can spur innovation, there's also a compelling argument for standardizing practices that enhance efficiency and accessibility for developers worldwide.


More to read

HTMX: Redefining Simplicity in Web Development

Discover the simplicity of web development with HTMX. Dive into practical examples that showcase HTMX's ability to enhance HTML for dynamic updates, form submissions, and real-time search—effortlessly and with minimal code. Learn how HTMX streamlines development, making it easier to create interactive, maintainable web applications.

Photo of Adil Haddaoui
Adil Haddaoui

Customizing JWT Session in NextAuth: A Practical Guide to Adding custom User Data

Unlock the full potential of NextAuth in your Next.js applications by learning how to seamlessly integrate additional user data into JWT sessions. This comprehensive guide demystifies the process and provides you with the missing pieces of the puzzle, ensuring your authentication flow is not only secure but also informative and tailored to your needs. Dive into our step-by-step tutorial and elevate your user sessions beyond the basics!

Photo of Adil Haddaoui
Adil Haddaoui