renderToPipeableStream
renderToPipeableStream
একটি React tree কে একটি pipeable Node.js Stream. এ রেন্ডার করে।
const { pipe, abort } = renderToPipeableStream(reactNode, options?)
- রেফারেন্স
- ব্যবহার
- React tree কে Node.js Stream এ HTML হিসেবে render করা
- Load হতে হতে আরো content stream করা
- Shell এ কী যাবে তা নির্দিষ্ট করা
- Server এ crash log করা
- Shell এর ভিতরের error থেকে রিকভার করা
- Shell এর বাইরের error থেকে রিকভার করা
- স্ট্যাটাস কোড সেট করা
- বিভিন্ন ধরনের error বিভিন্ন ভাবে হ্যান্ডেল করা
- Crawlers এবং static generation এর জন্য সব কনটেন্ট লোড হওয়া পর্যন্ত অপেক্ষা করা
- Server rendering বাতিল করা
রেফারেন্স
renderToPipeableStream(reactNode, options?)
আপনার React tree কে একটি Node.js Stream এ HTML হিসেবে রেন্ডার করতে renderToPipeableStream
কল করুন।
import { renderToPipeableStream } from 'react-dom/server';
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
}
});
Client-side এ, server-generated HTML কে ইন্টার্যাক্টিভ করতে hydrateRoot
কল করুন।
প্যারামিটারস
-
reactNode
: একটি React নোড যেটিকে আপনি HTML এ রেন্ডার করতে চান। উদাহরণস্বরূপ,<App />
এর মতো একটি JSX element । এটা প্রত্যাশিত যে, এটি পুরো ডকুমেন্ট কে ধারণ করবে, তাইApp
কম্পোনেন্টটির<html>
ট্যাগ রেন্ডার করার কথা। -
optional
options
: একটি object যাতে streaming options থাকবে।- optional
bootstrapScriptContent
: যদি প্রদান করা হয়, তাহলে এই string টি একটি inline<script>
tag এ থাকবে। - optional
bootstrapScripts
: page এ emit করার জন্য<script>
tag এর string URL গুলোর একটি array।hydrateRoot
কল করে এমন<script>
include করতে এটি ব্যবহার করুন। যদি আপনি client এ একেবারেই React রান করতে না চান তাহলে এটি বাদ দিন। - optional
bootstrapModules
:bootstrapScripts
এর মতো, কিন্তু এর পরিবর্তে<script type="module">
emit করে। - optional
identifierPrefix
: React এuseId
দ্বারা তৈরি ID এর জন্য ব্যবহৃত একটি string prefix। একই page এ একাধিক root ব্যবহার করার সময় conflict এড়াতে কাজে আসে।hydrateRoot
এ পাস করা prefix এর অনুরূপ হতে হবে। - optional
namespaceURI
: stream এর জন্য root namespace URI এর একটি string । ডিফল্টভাবে এটি regular HTML । SVG এর জন্য'http://www.w3.org/2000/svg'
অথবা MathML এর জন্য'http://www.w3.org/1998/Math/MathML'
পাস করুন। - optional
nonce
:script-src
Content-Security-Policy এর জন্য script গুলোকে allow করতে একটিnonce
string। - optional
onAllReady
: একটি callback যেটি সকল rendering কমপ্লিট হওয়ার পর ফায়ার হয়, shell এবং সকল অতিরিক্ত content সহ। crawler এবং static generation এর জন্য আপনিonShellReady
এর পরিবর্তে এটি ব্যবহার করতে পারেন। যদি আপনি এখানে streaming শুরু করেন, তাহলে আপনি কোনো progressive loading পাবেন না। stream টিতে final HTML থাকবে। - optional
onError
: একটি callback যেটি যেকোনো server error হলে ফায়ার হয়, recoverable হোক বা না হোক। By default, এটি শুধুconsole.error
কল করে। যদি আপনি crash report log করার জন্য এটি override করেন, তাহলে নিশ্চিত করুন যে আপনি তখনোconsole.error
কল করতে পারছেন। এছাড়াও shell emit হওয়ার আগে status code adjust করতে এটি ব্যবহার করতে পারেন। - optional
onShellReady
: একটি callback যেটি initial shell render হওয়ার ঠিক পরেই ফায়ার হয়। আপনি এখানে status code set করতে এবং streaming শুরু করতেpipe
কল করতে পারেন। React shell এর পর অতিরিক্ত content stream করবে inline<script>
tag সহ যেগুলো HTML loading fallback গুলোকে content দিয়ে replace করে। - optional
onShellError
: একটি callback যেটি initial shell render করতে error হলে ফায়ার হয়। এটি error কে argument হিসেবে receive করে। এখনো stream থেকে কোনো byte emit হয়নি, এবংonShellReady
বাonAllReady
কোনোটিই call হবে না, তাই আপনি একটি fallback HTML shell output করতে পারেন। - optional
progressiveChunkSize
: একটি chunk এ byte এর সংখ্যা। default heuristic সম্পর্কে আরো পড়ুন।
- optional
রিটার্নস
renderToPipeableStream
দুইটি method ওয়ালা একটি object return করেঃ
pipe
প্রদত্ত Writable Node.js Stream এ HTML output করে। streaming enable করতে চাইলেonShellReady
তেpipe
কল করুন, অথবা crawler এবং static generation এর জন্যonAllReady
তে কল করুন।abort
আপনাকে server rendering abort করতে এবং বাকিটা client এ render করতে দেয়।
ব্যবহার
React tree কে Node.js Stream এ HTML হিসেবে render করা
আপনার React tree কে একটি Node.js Stream এ HTML হিসেবে render করতে renderToPipeableStream
কল করুনঃ
import { renderToPipeableStream } from 'react-dom/server';
// The route handler syntax depends on your backend framework
app.use('/', (request, response) => {
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
}
});
});
root component এর সাথে, আপনাকে bootstrap <script>
path গুলোর একটি list প্রদান করতে হবে। আপনার root component টি যেন root <html>
tag সহ পুরো document return করে।
উদাহরণস্বরূপ, এটি এরকম দেখতে হতে পারেঃ
export default function App() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles.css"></link>
<title>My app</title>
</head>
<body>
<Router />
</body>
</html>
);
}
React doctype এবং আপনার bootstrap <script>
tag গুলো HTML stream এ inject করবেঃ
<!DOCTYPE html>
<html>
<!-- ... HTML from your components ... -->
</html>
<script src="/main.js" async=""></script>
Client এ, আপনার bootstrap script যেন hydrateRoot
কল করে পুরো document
hydrate করেঃ
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(document, <App />);
এটি server-generated HTML এ event listener attach করবে এবং এটিকে interactive করে তুলবে।
গভীরভাবে জানুন
Final asset URL গুলো (যেমন JavaScript এবং CSS file) প্রায়ই build এর পর hash করা হয়। উদাহরণস্বরূপ, styles.css
এর পরিবর্তে আপনি styles.123456.css
পেতে পারেন। Static asset filename hash করা নিশ্চিত করে যে একই asset এর প্রতিটি আলাদা build এর আলাদা filename থাকবে। এটি উপকারী কারণ এটি আপনাকে static asset এর জন্য safely long-term caching enable করতে দেয়: একটি নির্দিষ্ট নামের file এর content কখনো পরিবর্তন হবে না।
তবে, build এর পর পর্যন্ত যদি আপনি asset URL গুলো না জানেন, তাহলে source code এ সেগুলো রাখার কোনো উপায় নেই। উদাহরণস্বরূপ, আগের মতো JSX এ "/styles.css"
hardcode করা কাজ করবে না। এগুলো আপনার source code থেকে দূরে রাখতে, আপনার root component একটি prop হিসেবে পাস করা map থেকে আসল filename গুলো read করতে পারে:
export default function App({ assetMap }) {
return (
<html>
<head>
...
<link rel="stylesheet" href={assetMap['styles.css']}></link>
...
</head>
...
</html>
);
}
Server এ, <App assetMap={assetMap} />
render করুন এবং asset URL গুলো সহ আপনার assetMap
পাস করুনঃ
// You'd need to get this JSON from your build tooling, e.g. read it from the build output.
const assetMap = {
'styles.css': '/styles.123456.css',
'main.js': '/main.123456.js'
};
app.use('/', (request, response) => {
const { pipe } = renderToPipeableStream(<App assetMap={assetMap} />, {
bootstrapScripts: [assetMap['main.js']],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
}
});
});
যেহেতু আপনার server এখন <App assetMap={assetMap} />
render করছে, hydration error এড়াতে client এও assetMap
সহ এটি render করতে হবে। আপনি এইভাবে assetMap
serialize করে client এ পাস করতে পারেন:
// You'd need to get this JSON from your build tooling.
const assetMap = {
'styles.css': '/styles.123456.css',
'main.js': '/main.123456.js'
};
app.use('/', (request, response) => {
const { pipe } = renderToPipeableStream(<App assetMap={assetMap} />, {
// Careful: It's safe to stringify() this because this data isn't user-generated.
bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`,
bootstrapScripts: [assetMap['main.js']],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
}
});
});
উপরের উদাহরণে, bootstrapScriptContent
option একটি অতিরিক্ত inline <script>
tag যোগ করে যেটি client এ global window.assetMap
variable সেট করে। এটি client code কে একই assetMap
read করতে দেয়:
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(document, <App assetMap={window.assetMap} />);
Client এবং server উভয়েই একই assetMap
prop সহ App
render করে, তাই কোনো hydration error থাকবে না।
Load হতে হতে আরো content stream করা
Streaming ইউজারকে সকল ডাটা সার্ভারে লোড হওয়ার আগেই কন্টেন্ট দেখতে দেয়। উদাহরণস্বরূপ, একটি profile page এর কথা ভাবুন যেটি একটি cover, friend এবং photo সহ একটি sidebar, এবং post এর একটি লিস্ট দেখায়ঃ
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Sidebar>
<Friends />
<Photos />
</Sidebar>
<Posts />
</ProfileLayout>
);
}
ধরুন যে <Posts />
এর জন্য data লোড হতে কিছু সময় লাগে। স্বাভাবিকভাবে, আপনি post এর জন্য অপেক্ষা না করে user কে profile page এর বাকি content দেখাতে চাইবেন। এটি করতে, Posts
কে একটি <Suspense>
boundary তে wrap করুনঃ
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Sidebar>
<Friends />
<Photos />
</Sidebar>
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</ProfileLayout>
);
}
এটি React কে Posts
এর data লোড হওয়ার আগেই HTML streaming শুরু করতে বলে। React প্রথমে loading fallback (PostsGlimmer
) এর জন্য HTML পাঠাবে, এবং তারপর, যখন Posts
এর data লোডিং শেষ হবে, React বাকি HTML পাঠাবে একটি inline <script>
tag সহ যেটি লোডিং fallback কে সেই HTML দিয়ে replace করবে। ইউজারের দৃষ্টিকোণ থেকে, page প্রথমে PostsGlimmer
নিয়ে দেখাবে, পরে Posts
দিয়ে রিপ্লেস হবে।
আরও granular loading sequence তৈরি করতে আপনি আরও nested <Suspense>
boundary যোগ করতে পারেনঃ
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Suspense fallback={<BigSpinner />}>
<Sidebar>
<Friends />
<Photos />
</Sidebar>
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</Suspense>
</ProfileLayout>
);
}
এই উদাহরণে, React আরো আগে page streaming শুরু করতে পারে। শুধুমাত্র ProfileLayout
এবং ProfileCover
এর প্রথমে rendering শেষ করতে হবে কারণ তারা কোনো <Suspense>
boundary তে wrap করা নেই। তবে, যদি Sidebar
, Friends
, অথবা Photos
এর কিছু data load করতে হয়, React তার পরিবর্তে BigSpinner
fallback এর HTML পাঠাবে। তারপর, যত বেশি data লোড হবে, ততবেশি content প্রকাশিত হতে থাকবে যতক্ষণ না সবকিছু দৃশ্যমান হয়।
Streaming এর React নিজে browser এ load হওয়ার জন্য অথবা আপনার app interactive হওয়ার জন্য অপেক্ষা করার প্রয়োজন নেই। Server থেকে HTML content কোনো <script>
tag load হওয়ার আগেই একে একে প্রকাশিত হতে থাকবে।
HTML Streaming কিভাবে কাজ করে সে সম্পর্কে আরো পড়ুন।
Shell এ কী যাবে তা নির্দিষ্ট করা
আপনার app এর <Suspense>
boundary এর বাইরের যেকোনো অংশকে বলা হয় shell:
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Suspense fallback={<BigSpinner />}>
<Sidebar>
<Friends />
<Photos />
</Sidebar>
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</Suspense>
</ProfileLayout>
);
}
এটা ঠিক করে দেয় যে ইউজার প্রথমে কোন loading state টি দেখতে পারেঃ
<ProfileLayout>
<ProfileCover />
<BigSpinner />
</ProfileLayout>
যদি আপনি root এ পুরো app কে একটি <Suspense>
boundary তে wrap করেন, shell এ শুধু সেই spinner থাকবে। তবে, এটি একটি সুন্দর user experience না কারণ screen এ একটি বড় spinner দেখা আরো slow এবং বিরক্তিকর মনে হতে পারে একটু বেশি অপেক্ষা করে আসল layout দেখার চেয়ে। এই কারণেই সাধারণত আপনি <Suspense>
boundary গুলো এমনভাবে রাখবেন যাতে shell নুন্যতম কিন্তু সম্পূর্ণ মনে হয়—পুরো page layout এর একটি skeleton এর মতো।
onShellReady
callback পুরো shell render হওয়ার পর ফায়ার হয়। সাধারণত, আপনি তখনই streaming শুরু করবেনঃ
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
}
});
onShellReady
ফায়ার হওয়ার সময়ে, নেস্টেড <Suspense>
বাউন্ডারি এর কম্পোনেন্টগুলো তখনো ডাটা লোড করতে থাকতে পারে।
Server এ crash log করা
স্বাভাবিকভাবে, সার্ভারের সকল error console এ log হয়। আপনি ক্র্যাশ রিপোর্ট log করতে এই আচরণ পরিবর্তন করতে পারেন:
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
},
onError(error) {
console.error(error);
logServerCrashReport(error);
}
});
যদি আপনি একটি custom onError
implementation প্রদান করেন, উপরের মতো কনসোল-এ error log করতে ভুলবেন না।
Shell এর ভিতরের error থেকে রিকভার করা
এই উদাহরণে, shell এ ProfileLayout
, ProfileCover
, এবং PostsGlimmer
রয়েছেঃ
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</ProfileLayout>
);
}
যদি এই কম্পোনেন্টগুলো রেন্ডার করার সময় error হয়, React এর ক্লায়েন্ট-এ পাঠানোর জন্য কোনো বোধগম্য HTML থাকবে না। শেষ অবলম্বন হিসেবে সার্ভার রেন্ডারিং-এ নির্ভর করে না এমন একটি fallback HTML পাঠাতে onShellError
override করুনঃ
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
},
onShellError(error) {
response.statusCode = 500;
response.setHeader('content-type', 'text/html');
response.send('<h1>Something went wrong</h1>');
},
onError(error) {
console.error(error);
logServerCrashReport(error);
}
});
যদি shell generate করার সময় error হয়, onError
এবং onShellError
উভয়ই ফায়ার করবে। Error reporting এর জন্য onError
ব্যবহার করুন এবং fallback HTML document পাঠানোর জন্য onShellError
ব্যবহার করুন। আপনার fallback HTML একটি error page হতে হবে এমন না। বরং, আপনি একটি বিকল্প shell z যুক্ত করতে পারেন যেটি শুধু client এ আপনার অ্যাপ রেন্ডার করে।
Shell এর বাইরের error থেকে রিকভার করা
এই উদাহরণে, <Posts />
component টি <Suspense>
এর মধ্যে রয়েছে তাই এটি shell এর অংশ নয়:
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</ProfileLayout>
);
}
যদি Posts
component বা এর ভিতরে কোথাও একটি error ঘটে, React এর থেকে রিকভার করার চেষ্টা করবেঃ
- এটি নিকটতম
<Suspense>
boundary (PostsGlimmer
) এর জন্য loading fallback HTML এ emit করবে। - এটি আর সার্ভারে
Posts
কনটেন্ট রেন্ডার করার চেষ্টা করবে না। - যখন JavaScript কোড ক্লায়েন্টে লোড হয়, React ক্লায়েন্টে
Posts
রেন্ডার করার জন্য পুনরায় চেষ্টা করবে।
যদি Posts
ক্লায়েন্টে রেন্ডার করার জন্য পুনরায় চেষ্টা করাও ব্যর্থ হয়, React ক্লায়েন্টে error টি throw করবে। রেন্ডারিংয়ের সময় throw হওয়া সব error এর মতো, closest parent error boundary নির্ধারণ করে কিভাবে ইউজারের কাছে error টি উপস্থাপন করতে হবে। বাস্তবে, এর মানে হল যে ইউজার একটি লোডিং ইন্ডিকেটর দেখতে পাবে যতক্ষণ না এটি নিশ্চিত হয় যে error টি recoverable না।
যদি retrying এর সময় client এ Posts
render করা সফল হয়, সার্ভারের loading fallback টি client এ রেন্ডার করা আউটপুট দিয়ে replace হবে। User জানবে না যে server error হয়েছিল। তবে, server onError
callback এবং client onRecoverableError
callback গুলো ফায়ার হবে যাতে আপনি error সম্পর্কে notified হতে পারেন।
স্ট্যাটাস কোড সেট করা
Streaming একটি tradeoff নিয়ে আসে। আপনি যত তাড়াতাড়ি সম্ভব পেজ streaming শুরু করতে চান যাতে ইউজার তাড়াতাড়ি কন্টেন্ট দেখতে পায়। তবে, একবার streaming শুরু করলে, আপনি আর রেসপন্স স্ট্যাটাস কোড সেট করতে পারবেন না।
আপনার app কে shell (সকল <Suspense>
boundary এর উপরে) এবং বাকি content এ ভাগ করে, আপনি ইতিমধ্যে এই সমস্যার একটি অংশ সমাধান করেছেন। যদি shell এ error হয়, আপনি onShellError
callback পাবেন যা আপনাকে error status code সেট করতে দেয়। অন্যথায়, আপনি জানেন যে app client এ recover হতে পারে, তাই আপনি “OK” পাঠাতে পারেন।
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.statusCode = 200;
response.setHeader('content-type', 'text/html');
pipe(response);
},
onShellError(error) {
response.statusCode = 500;
response.setHeader('content-type', 'text/html');
response.send('<h1>Something went wrong</h1>');
},
onError(error) {
console.error(error);
logServerCrashReport(error);
}
});
যদি shell এর বাইরের (অর্থাৎ কোনো <Suspense>
boundary এর ভিতরে থাকা) কোনো component একটি error throw করে, React রেন্ডারিং থামাবে না। এর মানে onError
callback ফায়ার হবে, কিন্তু তারপরও আপনি onShellError
এর বদলে onShellReady
পাবেন। কারণ React ওই error থেকে client এ রিকভার করার চেষ্টা করবে, উপরে যেমন ব্যাখ্যা করা হয়েছে।
তবে চাইলে, কোনো কিছু error হয়েছে—এই তথ্যটি ব্যবহার করে আপনি স্ট্যাটাস কোড সেট করতে পারেন :
let didError = false;
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.statusCode = didError ? 500 : 200;
response.setHeader('content-type', 'text/html');
pipe(response);
},
onShellError(error) {
response.statusCode = 500;
response.setHeader('content-type', 'text/html');
response.send('<h1>Something went wrong</h1>');
},
onError(error) {
didError = true;
console.error(error);
logServerCrashReport(error);
}
});
এটি শুধুমাত্র initial shell কনটেন্ট তৈরি হওয়ার সময় shell-এর বাইরে ঘটে যাওয়া error গুলো ধরবে, তাই এটি পূর্ণাঙ্গ (exhaustive) নয়। যদি কোনো নির্দিষ্ট কনটেন্টে error হয়েছে কি না জানা অত্যন্ত গুরুত্বপূর্ণ হয়, তাহলে সেই কনটেন্টকে shell-এর ভেতরে নিয়ে আসুন।
বিভিন্ন ধরনের error বিভিন্ন ভাবে হ্যান্ডেল করা
আপনি নিজস্ব Error
subclasses তৈরি করতে পারেন এবং কোন error throw হয়েছে তা যাচাই করতে instanceof
অপারেটর ব্যবহার করতে পারেন। উদাহরণ হিসেবে, আপনি একটি custom NotFoundError
ডিফাইন করে আপনার component থেকে throw করতে পারেন। তারপর আপনার onError
, onShellReady
, এবং onShellError
callback গুলো error এর টাইপ অনুযায়ী ভিন্ন ভিন্ন কাজ করতে পারে:
let didError = false;
let caughtError = null;
function getStatusCode() {
if (didError) {
if (caughtError instanceof NotFoundError) {
return 404;
} else {
return 500;
}
} else {
return 200;
}
}
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.statusCode = getStatusCode();
response.setHeader('content-type', 'text/html');
pipe(response);
},
onShellError(error) {
response.statusCode = getStatusCode();
response.setHeader('content-type', 'text/html');
response.send('<h1>Something went wrong</h1>');
},
onError(error) {
didError = true;
caughtError = error;
console.error(error);
logServerCrashReport(error);
}
});
মনে রাখবেন, একবার shell emit হয়ে এবং streaming শুরু হয়ে গেলে আপনি আর স্ট্যাটাস কোড পরিবর্তন করতে পারবেন না।
Crawlers এবং static generation এর জন্য সব কনটেন্ট লোড হওয়া পর্যন্ত অপেক্ষা করা
Streaming একটি ভালো user experience দেয় কারণ ইউজার কনটেন্ট প্রস্তুত হওয়ার সাথে সাথে তা দেখতে পারে।
তবে, যখন কোনো crawler আপনার পেজ ভিজিট করে, অথবা আপনি build time এ পেজগুলো pre-generate করছেন, তখন আপনি ধাপে ধাপে দেখানোর বদলে আগে সব data লোড হতে দিয়ে সম্পূর্ণ final HTML একবারে তৈরি করতে চাইতে পারেন।
onAllReady
callback ব্যবহার করে আপনি সব কনটেন্ট লোড হওয়া পর্যন্ত অপেক্ষা করতে পারেন:
let didError = false;
let isCrawler = // ... depends on your bot detection strategy ...
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
if (!isCrawler) {
response.statusCode = didError ? 500 : 200;
response.setHeader('content-type', 'text/html');
pipe(response);
}
},
onShellError(error) {
response.statusCode = 500;
response.setHeader('content-type', 'text/html');
response.send('<h1>Something went wrong</h1>');
},
onAllReady() {
if (isCrawler) {
response.statusCode = didError ? 500 : 200;
response.setHeader('content-type', 'text/html');
pipe(response);
}
},
onError(error) {
didError = true;
console.error(error);
logServerCrashReport(error);
}
});
একজন সাধারণ ভিজিটর ধাপে ধাপে (progressively) লোড হওয়া কনটেন্টের stream পাবে। একটি crawler সব data লোড হওয়ার পর পুরো final HTML পাবে। তবে এর মানে crawler-কে সব data এর জন্য অপেক্ষা করতে হবে—এর কিছু হয়তো ধীরগতির বা error-প্রবণ হতে পারে। আপনার অ্যাপের ধরন অনুযায়ী চাইলে আপনি crawlers এর জন্যও শুধু shell পাঠাতে পারেন।
Server rendering বাতিল করা
একটি নির্দিষ্ট সময় (timeout) পর আপনি সার্ভার রেন্ডারিং-কে জোর করে “হাল ছেড়ে দিতে” বাধ্য করতে পারেন:
const { pipe, abort } = renderToPipeableStream(<App />, {
// ...
});
setTimeout(() => {
abort();
}, 10000);
React বাকি যেসব loading fallback আছে সেগুলোকে HTML আকারে flush করবে এবং বাকিটা ক্লায়েন্ট-এ রেন্ডার করার চেষ্টা করবে।