page.tsx
Overview
page.tsx is a React client component that demonstrates partial hydration using React’s Suspense mechanism combined with a simulated asynchronous delay. Its primary function is to delay the hydration of its content by throwing a Promise that resolves after 2 seconds, causing React to suspend rendering until the delay completes. After the delay, it fetches data via a custom hook (useData) integrated with SWR for data fetching and caching, and uses a debugging hook (useDebugHistory) to track and visualize data changes during hydration.
This component exemplifies streaming SSR workflows where parts of the UI hydrate asynchronously and gradually, improving perceived performance and reducing blocking on the client.
Detailed Explanation
Key Concepts in page.tsx
Suspense Triggering via Promise Throwing: The component throws a Promise (
susp) if it hasn't resolved yet, causing React Suspense boundaries to suspend rendering until the Promise resolves.Data Fetching Post-Suspense: Once the delay resolves, it invokes
useData(), a custom hook that fetches data asynchronously and returns it.Debug History Tracking: The
useDebugHistoryhook attaches a ref to a DOM element to log or track the data changes, useful for debugging hydration and data consistency.
Variables
let resolved = false
const susp = new Promise(res => {
setTimeout(() => {
resolved = true
res(true)
}, 2000)
})
resolved: A boolean flag indicating if the simulated delay Promise has resolved.susp: A Promise that resolves after 2000 milliseconds (2 seconds) to simulate asynchronous readiness.
Component: Page
export default function Page() { ... }
Purpose
Page is a React functional component that:
Suspends rendering until the 2-second delay (
susp) resolves.After delay resolution, fetches data asynchronously.
Renders UI with the fetched data.
Uses a debug hook to track data changes.
Implementation Details
Suspense Boundary Trigger:
if (!resolved) { throw susp }If the Promise
susphas not resolved, the component throws it.React Suspense catches this Promise and suspends rendering this component until it resolves.
This implements delayed hydration on the client side and partial rendering on the server side.
Data Fetching:
const { data } = useData()useDatais a custom hook (imported from./use-data) that leverages SWR to fetch data asynchronously.It returns an object containing the
datafield representing the fetched content.
Debug History Hook:
const debugRef = useDebugHistory(data, 'second history:')useDebugHistoryis imported from~/lib/use-debug-history.It returns a React ref (
debugRef) attached to a<div>element.This hook tracks the history of data changes for debugging hydration and rendering flows.
Rendering:
return ( <div> <div ref={debugRef}></div> <>second data (delayed hydration):{data || 'undefined'}</> </div> )Renders a container with:
An empty
<div>bound todebugReffor debug tracking.A fragment displaying the label and the fetched
dataor'undefined'if data is absent.
Usage Example
import Page from './page'
function App() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<Page />
</React.Suspense>
)
}
Wrap
Pagein a React Suspense boundary with a fallback UI.When
Pagethrows thesuspPromise, Suspense will show the fallback.After 2 seconds,
Pageresumes rendering and fetches/displays data.
Interactions with Other Files and System Components
useDataHook (./use-data): Handles asynchronous data fetching using SWR. It abstracts fetching logic and caching, ensuring data is available post-suspense resolution.useDebugHistoryHook (~/lib/use-debug-history): Attaches debugging behavior to track hydration state and data changes tied to this component’s lifecycle.React Suspense and Concurrent Rendering: Relies on React’s Suspense mechanism to coordinate delayed rendering and partial hydration triggered by throwing Promises.
Server-Side Rendering and Streaming System: This file is part of a system demonstrating streaming SSR and partial hydration. It models delayed hydration by simulating a 2-second async boundary.
Important Implementation Details and Algorithms
Suspense-Based Delayed Hydration
The component uses a manual Promise throwing pattern to trigger React Suspense. This is a recognized React concurrent mode pattern to suspend rendering until async conditions are met.
The Promise
suspis created once at module scope and resolves after 2 seconds, simulating asynchronous readiness.This approach allows React to stream the server-rendered HTML and hydrate it on the client in stages, improving perceived performance by deferring hydration of the component’s UI.
Data Fetching Integration
The
useDatahook likely throws its own Promises internally if data is not ready, which works seamlessly with React Suspense.This file ensures that data fetching occurs only after the initial 2-second delay, mimicking scenarios where hydration depends on multiple asynchronous conditions.
Debug History Tracking
The
useDebugHistoryhook helps developers confirm that data updates and hydration occur as expected by logging changes or attaching metadata to the DOM.
Mermaid Component Diagram
componentDiagram
component Page {
+Page()
}
component useData {
+useData(): { data: string | undefined }
}
component useDebugHistory {
+useDebugHistory(data, label): React.RefObject
}
component susp {
+Promise (2s delay)
+resolved: boolean
}
Page --> susp : throws Promise to suspend rendering
Page --> useData : calls to fetch data post-delay
Page --> useDebugHistory : tracks data changes for debugging
Summary
page.tsxis a client-side React component designed to demonstrate partial hydration using React Suspense.It suspends rendering by throwing a 2-second delay Promise, simulating asynchronous readiness.
After delay resolution, it fetches data asynchronously and renders it.
It integrates with debugging utilities to monitor hydration state.
This file exemplifies React streaming SSR patterns enabling staggered hydration for performance optimization.
Appendix: Relevant Code Snippet
'use client'
import { useDebugHistory } from '~/lib/use-debug-history'
import useData from './use-data'
let resolved = false
const susp = new Promise(res => {
setTimeout(() => {
resolved = true
res(true)
}, 2000)
})
export default function Page() {
if (!resolved) {
throw susp
}
const { data } = useData()
const debugRef = useDebugHistory(data, 'second history:')
return (
<div>
<div ref={debugRef}></div>
<>second data (delayed hydration):{data || 'undefined'}</>
</div>
)
}
References
React Suspense for Data Fetching: https://reactjs.org/docs/concurrent-mode-suspense.html
SWR Data Fetching Library: https://swr.vercel.app/
Streaming SSR and Partial Hydration Concepts (project internal docs)