TIL, 2023-07-25, React Server Components
How React 18 Improves Application Performance
- Any task that takes more than 50ms to run is a long task.
- This comes from the fact that devices must create a new frame every 16ms (60fps) to maintain a smooth visual experience, however, they must also perform other tasks, such as responding to JS.
- 50ms means the devices has 33.33ms for the device to perform other tasks.
- Total blocking time:
- Between the First Contentful Paint and Time to Interactive.
- Interaction to next paint: Time from a user’s first interaction with the page to when the interaction is visible on-screen, the next paint.
- Visual update in React is divided into two phases: the render phase and the commit phase.
- Render - computation phase where React elements are reconciled with the existing DOM.
- React calculates the differences between the current DOM and the new React component tree and prepares the necessary updates.
- Synchronous render - React give the same priority to all elements within a component tree.
- During this time, the main thread is blocked. Example: search field where you do
setText
- there is a delay.
- During this time, the main thread is blocked. Example: search field where you do
- React 18: New concurrent renderer that exposes ways for us to mark certain renders as non-urgent.
- React can pause and resume the rendering of one or multiple component trees to achieve the most optimal user experience.
Transitions
- We can mark update as non-urgent by using
startTransition
. - When a transition starts, the concurrent renderer prepares the new tree in the background.
import { useTransition } from "react";
function Button() {
const [isPending, startTransition] = useTransition();
return (
<button
onClick={() => {
urgentUpdate();
startTransition(() => {
nonUrgentUpdate()
})
}}
>...</button>
)
}
- Fix using
useTransition
import React, { useState, useTransition } from "react";
import CityList from "./CityList";
export default function SearchCities() {
const [text, setText] = useState("Am");
const [searchQuery, setSearchQuery] = useState(text);
const [isPending, startTransition] = useTransition();
return (
<main>
<h1><code>startTransition</code></h1>
<input
type="text"
value={text}
onChange={(e) => {
setText(e.target.value)
startTransition(() => { <- React renders the new tree on each keystroke, but starts preparing
setSearchQuery(e.target.value) <- the new version in memory while the curren UI remains responsive to
<- current user input.
})
}} />
<CityList searchQuery={searchQuery} />
</main>
);
};
React Server Components
- Send the actual serialized component tree to the client. Client-side React renderer performantly reconstructs the React component tree without sending the HTML file or JS bundle.
- When you do
use client
, you tell the bundler to “add this component and its imports” to the client bundle and React to hydrate the tree client-side to act interactivity.
React Suspense
- We delay the rendering of a component until certain conditions are met, like data being loaded.
Data Fetching
cache
that remembers the result of the wrapped function call.
Introducing Zero-Bundle-Size React Server Components
react-fetch
API- Dependencies in the server component are not a part of the bundle.
- A client component can receive props from the server
- Asking to re-render the server component:
- Set a location and re-render the server tree from the root.
- Server components vs SSR:
- Clicking on the note to show on the right - keeps all client-rendered components don’t get blown away.
- Shared component: Can render on the server or the component, whoever renders it dictates.
- Suspense + Server components
- Transitions - Defining component in a transition means you respond immediately.
- Which components are on the server and which are on the client?
- React IO libraries
- react-fs, react-pg, react-fetch
Musings
- So most of our app should honestly be server-rendered?
- Ex: Listing page, maybe the search input, tabs, etc. is client-rendered, but banner, listing items etc. are server-rendered.
- Then we should use
useTransition
more. - So this is why we shouldn’t probably be super invested in react-query, or at least have it alongside server components