← वापस Vintage server rack in a dimly lit room with warm golden light through a narrow window, 35mm film grain

React Server Components समझाया गया बिना हाथ लहराए

2023 की शुरुआत में एक क्लाइंट ने, Canary Wharf में एक फिनटेक स्टार्टअप, मुझे एक Next.js 13 कोडबेस सौंपा जो एक पिछली एजेंसी ने बनाया था। यह नई app/ डायरेक्टरी का इस्तेमाल कर रहा था। अच्छा। लेकिन हाइड्रेशन एरर हर जगह थी, बंडल 340 KB gzipped था और पुरानी टीम के किसी को नहीं पता था कि उन्होंने "use client" को हर फाइल पर क्यों डाल दिया। जब मैंने उन्हें React Server Components के बारे में पूछा, तो वे कहते हैं: "ओह हाँ, हम उन्हें इस्तेमाल करते हैं।" वे उन्हें इस्तेमाल नहीं कर रहे थे।

यही RSC के साथ अभी की समस्या है। सब कहते हैं कि वे समझते हैं। लगभग कोई भी असल में समझता नहीं है। तो मैं आपको वह संस्करण दूँ जो मैं चाहता था कि 2023 की शुरुआत में मौजूद हो।

RSC असल में क्या हैं (मार्केटिंग संस्करण नहीं)

React Server Components ऐसे कंपोनेंट हैं जो केवल सर्वर पर चलते हैं और उनका JavaScript कभी ब्राउज़र को नहीं भेजा जाता। बस।

"सर्वर-साइड रेंडरिंग" नहीं। "प्री-रेंडरिंग" नहीं। ये चीजें RSC से पहले थीं और वे अलग तरीके से काम करती हैं। पारंपरिक SSR के साथ (जो Next.js pages router करता है), आपके कंपोनेंट सर्वर पर HTML बनाने के लिए रेंडर होते हैं लेकिन उसी JavaScript क्लाइंट को भेजी जाती है ताकि React पेज को "हाइड्रेट" कर सके, इवेंट लिसनर जोड़ सके और नियंत्रण ले सके।

RSC दूसरे भाग को पूरी तरह छोड़ देते हैं। कंपोनेंट सर्वर पर चलता है, रेंडर होता है, अपना आउटपुट क्लाइंट को सीरियलाइज़्ड पेलोड के रूप में भेजता है, और फिर... बस। उस कंपोनेंट के लिए कोई JavaScript कभी ब्राउज़र में नहीं उतरता।

व्यावहारिक प्रभाव: अगर आपके पास एक <ProductDescription> कंपोनेंट है जो कुछ टेक्स्ट रेंडर करने के लिए 40 KB मार्कडाउन पार्सर का इस्तेमाल करता है और आप इसे सर्वर कंपोनेंट बनाते हैं, तो वह 40 KB कभी यूज़र को नहीं जाता। पार्स किया हुआ HTML जाता है। बस।

React टीम की मूल RFC असल में पढ़ने लायक है अगर आप पूरी वजह जानना चाहते हैं। यह सघन है लेकिन ईमानदार है।

वह मानसिक मॉडल जिसने आखिरकार मेरे लिए चीजों को स्पष्ट किया

अपने कंपोनेंट ट्री को दो अलग-अलग दुनियाओं के रूप में सोचें जो एक साथ सिलाई गई हैं।

वर्ल्ड 1 (सर्वर)। आपके डेटाबेस, फाइलसिस्टम, एनवायरनमेंट वेरिएबल्स और सीक्रेट्स तक पूरी एक्सेस है। useState, useEffect या कोई भी ब्राउजर API नहीं चला सकते। ईवेंट लिसनर्स नहीं जोड़ सकते।

वर्ल्ड 2 (क्लाइंट)। ब्राउजर में चलता है। सभी React हुक्स जो आप जानते हैं, सब का उपयोग कर सकते हैं। सीधे अपने डेटाबेस से बात नहीं कर सकते। बजाय इसके API को रिक्वेस्ट भेजते हैं।

RSC से पहले, हर कंपोनेंट World 2 में रहता था, भले ही वह पहले सर्वर पर रेंडर हो। RSC के साथ, आप अब कंपोनेंट्स को World 1 में explicitly रख सकते हैं। ये कंपोनेंट्स World 2 कंपोनेंट्स को चाइल्ड्रन के रूप में रेंडर कर सकते हैं, उन्हें serialisable प्रॉप्स पास कर सकते हैं। लेकिन World 2 कंपोनेंट्स World 1 कंपोनेंट्स को रेंडर नहीं कर सकते। बाउंड्री एक-तरफा है।

यहीं लोग भ्रमित होते हैं: आप कोई डायरेक्टिव नहीं लगाते कि कोई सर्वर कंपोनेंट बन जाए। Next.js app/ डायरेक्टरी में, सब कुछ डिफ़ॉल्ट रूप से सर्वर कंपोनेंट है। आप "use client" के साथ क्लाइंट में opt-in करते हैं। ज्यादातर लोगों की धारणा से उलट।

Seahawk के पास पिछले साल एक प्रोजेक्ट था, एक UK रिटेलर के लिए एक बड़ा ई-कॉमर्स कैटलॉग, जहाँ हमने 60 के करीब कंपोनेंट्स का ऑडिट किया। पता चला कि उनमें से करीब 40 में बिना किसी कारण "use client" था। इसे हटाने से JavaScript बंडल में करीब 28% की कटौती हुई। एक दोपहर की मेहनत।

आप क्या कर सकते हैं और क्या नहीं (एक ठोस ब्रेकडाउन)

सर्वर कंपोनेंट्स कर सकते हैं:

  • async/await से सीधे डेटा fetch करें, कोई useEffect नहीं, कोई loading states नहीं, बस const data = await db.query(...)
  • भारी सर्वर-ओनली लाइब्रेरीज import करें (जैसे frontmatter parsing के लिए gray-matter या PDF generators) बिना क्लाइंट बंडल को छुए
  • Node के fs मॉड्यूल से फाइलसिस्टम पढ़ें
  • एनवायरनमेंट वेरिएबल्स एक्सेस करें जिन्हें आप ब्राउजर को expose नहीं करना चाहते
  • डेटा को क्लाइंट कंपोनेंट्स को प्रॉप्स के रूप में नीचे पास करें

सर्वर कंपोनेंट्स नहीं कर सकते:

  • useState या useReducer का उपयोग करें
  • useEffect या useLayoutEffect का उपयोग करें
  • ईवेंट हैंडलर्स अटैच करें (कोई onClick नहीं, कोई onChange नहीं)
  • ब्राउजर API उपयोग करें (window, document, localStorage)
  • React Context को सीधे उपयोग करें (हालांकि इसके चारों ओर कुछ patterns हैं)

क्लाइंट कंपोनेंट्स ऊपर की हर चीज कर सकते हैं जो सर्वर कंपोनेंट्स नहीं कर सकते, लेकिन:

  • वे सीधे आपके डेटाबेस को कॉल नहीं कर सकते
  • वे सर्वर-ओनली पैकेजेज का उपयोग नहीं कर सकते
  • उनका कोड ब्राउज़र में जाता है

यह लाइन सिर्फ परफॉर्मेंस के बारे में नहीं है। यह इस बारे में है कि कोड कहाँ चलता है और उसके पास क्या एक्सेस है। यह "तेज़" बनाम "धीमा" सोचने की तुलना में ज़्यादा उपयोगी है।

डेटा फेचिंग वह जगह है जहाँ RSCs असली चमक दिखाते हैं

यह वह हिस्सा है जो मुझे सच में पसंद है। RSCs से पहले, Next.js ऐप में डेटा फेच करना आमतौर पर इनमें से एक मतलब था: getServerSideProps, getStaticProps, क्लायंट-साइड useEffect कॉल, या तीनों का कोई कॉम्बिनेशन कस्टम हुक के साथ लोडिंग स्टेट मैनेज करने के लिए। गड़बड़ा हुआ।

RSCs के साथ, आप सिर्फ... फेच करते हैं। कंपोनेंट के अंदर। टॉप लेवल पर।

`` async function ProductPage({ id }) { const product = await getProduct(id); // calls your DB directly return <ProductDetails product={product} />; } ``

पेज-लेवल फंक्शन से कोई प्रॉप ड्रिलिंग नहीं। डेटा के लिए कोई लोडिंग स्पिनर नहीं जो आगमन पर तैयार हो सकता था। कंपोनेंट async है, यह अपनी ज़रूरत की चीज़ें await करता है, और रेंडर करता है।

और यहीं से वास्तव में दिलचस्प हो जाता है: क्योंकि हर server component अपना डेटा स्वतंत्र रूप से फेच कर सकता है, आप पुरानी "God component" समस्या से बचते हैं जहाँ एक top-level फंक्शन को पूरे पेज के लिए सभी डेटा ऑर्केस्ट्रेट करने पड़ते थे। कंपोनेंट्स सेल्फ-कंटेनड बन जाते हैं। पैरेलल फेचिंग naturally होती है जब आप Promise.all() को await करते हैं या जब sibling कंपोनेंट्स स्वतंत्र रूप से फेच करते हैं।

मैंने यह पैटर्न पिछली स्प्रिंग बर्मिंघम में एक लॉजिस्टिक्स फर्म के लिए एक डैशबोर्ड प्रोजेक्ट पर use किया। हर widget, शिपमेंट stats, delay alerts, cost summary, अपना ख़ुद का async server component था। कोई shared लोडिंग state नहीं, कोई prop drilling नहीं, कोई race conditions नहीं। पेज 2.1-सेकंड LCP से 0.9 सेकंड पर गया। सिर्फ RSCs की वजह से नहीं, लेकिन RSCs ने सही architecture तक पहुँचना कहीं आसान बना दिया।

रेंडरिंग पाइपलाइन (असली में क्या होता है)

जब कोई यूज़र Next.js 14 ऐप में app/ राउटर का use करते हुए एक पेज request करता है, तो यहाँ rough sequence है:

  1. Next.js आपके server components को सर्वर पर चलाता है
  2. वे कंपोनेंट्स एक special format produce करते हैं जिसे React Server Component Payload (RSC Payload) कहते हैं, raw HTML नहीं, बल्कि UI tree का एक serialised description
  3. Next.js उस payload को use करके initial HTML generate करता है (पहली पेंट के लिए)
  4. वह HTML ब्राउज़र को भेजा जाता है
  5. RSC Payload भी भेजा जाता है, और client React runtime इसे tree में सिर्फ client components को hydrate करने के लिए use करता है
  6. Client components को उनका JavaScript मिलता है, वे hydrate होते हैं, और interactive बन जाते हैं

Step 5 ही है जो RSCs को plain SSR से अलग बनाता है। client server components को re-render नहीं करता। यह बस payload का use करके तस्वीर भरता है और फिर hydration की कोशिश को interactive bits पर focus करता है।

Next.js डॉक्स on rendering payload format को ज़्यादा ज़्यादातर ब्लॉग posts की तुलना में बेहतर explain करते हैं, अगर आप गहराई में जाना चाहते हैं।

जहाँ RSCs टूट जाते हैं (ईमानदारी से Criticism)

ठीक है। चलिए ऐसा नहीं दिखावा करते कि यह सब सूरज है।

"use client" boundary cascade होती है। जिस लमहे आप किसी component पर "use client" लगाते हैं, हर component जिसे वह import करता है वह भी client-side बन जाता है। अगर आप सावधान नहीं हैं, तो एक onClick handler एक surprisingly large subtree को client bundle में खींच सकता है। मैंने यह टीम के junior devs को कई बार bite करते देखा है।

Context painful है। React Context server components में काम नहीं करता। अगर आपने अपनी ऐप architecture को Context के चारों ओर theming, auth state, या global config के लिए built किया है, तो आपको restructure करना होगा। workarounds हैं (values को props के रूप में pass करें, एक client wrapper use करें), लेकिन यह friction है जो आपके पास पहले नहीं था।

Debugging harder है। Server component errors browser console में same तरीके से show नहीं होते। error boundary behaviour अलग है। आपको server logs check करने हैं। dealbreaker नहीं है, लेकिन अगर आप सभी errors के DevTools में रहने के आदी हैं, तो एक learning curve की उम्मीद करें।

Third-party libraries अक्सर ready नहीं होती हैं। कोई भी library जो hooks या browser APIs को hood के अंदर use करती है, server component में टूट जाएगी। आप date picker या animation library use करने के लिए बस "use client" files में चीज़ों को wrap करते समय समय खर्च करेंगे। React ecosystem catch up कर रहा है लेकिन 2024 तक यह अभी भी patchy है।

ईमानदारी से कहूँ तो, एक सामान्य मार्केटिंग साइट या कंटेंट ब्लॉग के लिए आपको RSCs की बिल्कुल जरूरत नहीं हो सकती। अगर आपकी टीम pages router के साथ सहज है और आपका परफॉर्मेंस ठीक है, तो सिर्फ RSC सपोर्ट के लिए अपग्रेड करना दर्द के काबिल नहीं है। मैंने क्लाइंट्स को माइग्रेट करने से एक से अधिक बार रोका है।

मौजूदा प्रोजेक्ट पर RSCs को अपनाने के लिए व्यावहारिक सलाह

अगर आप अपनी मौजूदा Next.js ऐप को app/ डायरेक्टरी में ले जा रहे हैं, तो यह वह क्रम है जिसे मैं फॉलो करूँगा:

  1. लीफ कंपोनेंट्स से शुरू करें। वे कंपोनेंट्स जो डेटा दिखाते हैं लेकिन इंटरैक्शन हैंडल नहीं करते, सबसे आसान जीत हैं। पहले उन्हें सर्वर कंपोनेंट्स बनाएँ।
  2. अपनी असली इंटरैक्टिविटी सतह की पहचान करें। आमतौर पर यह आपके सोचने से छोटी होती है। फॉर्म्स, मॉडल्स, ड्रॉपडाउन्स। यह आपका "use client" इलाका है।
  3. `"use client"` को जितना हो सके नीचे धकेलें। जो बटन फॉर्म सबमिट करता है वह एक क्लाइंट कंपोनेंट होना चाहिए। उसके चारों ओर का फॉर्म लेआउट शायद होना नहीं चाहिए।
  4. [@next/bundle-analyzer](https://www.npmjs.com/package/@next/bundle-analyzer) से अपने बंडल को ऑडिट करें। इससे पहले और बाद में चलाएँ। अगर आप कोई सार्थक बंडल कमी नहीं देख रहे हैं, तो आप शायद अभी भी क्लाइंट को बहुत कुछ भेज रहे हैं।
  5. सर्वर-ओनली मॉड्यूल्स शेयर न करें। npm से server-only इंस्टॉल करें और किसी भी मॉड्यूल के शीर्ष पर इसे इम्पोर्ट करें जो कभी ब्राउज़र तक नहीं पहुँचना चाहिए। अगर कोई क्लाइंट-साइड से इसे इम्पोर्ट करने की कोशिश करता है तो यह बिल्ड टाइम पर एरर फेंकेगा।

server-only पैकेज एक छोटी चीज़ है लेकिन इसने हमें एक हेल्थकेयर क्लाइंट प्रोजेक्ट पर एक शर्मनाक डेटा लीक से बचाया। एक डेवलपर ने गलती से एक डेटाबेस यूटिलिटी को एक कंपोनेंट में इम्पोर्ट कर दिया जिसे बाद में "use client" मार्क किया गया। पैकेज ने इसे बिल्ड टाइम पर पकड़ा। इसे यूज़ करें।

FAQ

क्या React Server Components वही है जो SSR है?

नहीं। SSR (server-side rendering) कंपोनेंट्स को सर्वर पर HTML में रेंडर करता है लेकिन हाइड्रेशन के लिए कंपोनेंट JavaScript को अभी भी क्लाइंट को भेजता है। RSCs सर्वर पर रेंडर होते हैं और उनका JavaScript कभी ब्राउज़र में नहीं जाता। SSR और RSCs एक साथ काम कर सकते हैं, और Next.js app/ डायरेक्टरी में, वे करते भी हैं, लेकिन वे अलग-अलग समस्याओं को हल करते हैं।

क्या मुझे React Server Components यूज़ करने के लिए Next.js की जरूरत है?

तकनीकी रूप से नहीं, लेकिन ज्यादातर टीम्स के लिए व्यावहारिक रूप से हाँ। RSCs को एक ऐसे फ्रेमवर्क की जरूरत है जो सर्वर इनफ्रास्ट्रक्चर, रूटिंग, और RSC पेलोड पाइपलाइन को हैंडल करे। Next.js 13+ with app/ डायरेक्टरी अभी सबसे प्रोडक्शन-रेडी विकल्प है। Remix का एक अलग मॉडल है। अपना सेटअप रोल करना संभव है लेकिन अगर आप खुद एक फ्रेमवर्क बना रहे हैं तो यह आपके समय का अच्छा उपयोग नहीं है।

क्या मैं एक ही पेज में सर्वर और क्लाइंट कंपोनेंट्स को मिक्स कर सकता हूँ?

हाँ, और वही तो बात है। एक पेज एक सर्वर कंपोनेंट हो सकता है (डेटा फेच करता है, कोई JS नहीं भेजता), एक सर्च इनपुट के लिए एक क्लाइंट कंपोनेंट रखता हो, जो चाइल्ड्रन प्रॉप के माध्यम से पास किए गए सर्वर कंपोनेंट्स को चाइल्ड्रन के रूप में रखता हो। ट्री इंटरलीव होता है। बस याद रखें: सर्वर कंपोनेंट्स क्लाइंट कंपोनेंट्स को रेंडर कर सकते हैं, लेकिन क्लाइंट कंपोनेंट्स सीधे सर्वर कंपोनेंट्स को रेंडर नहीं कर सकते।

मेरे मौजूदा कस्टम हुक्स का क्या होता है?

कस्टम हुक्स जो useState, useEffect, या ब्राउज़र APIs यूज़ करते हैं, केवल क्लाइंट कंपोनेंट्स में चल सकते हैं। आपको उन्हें फिर से लिखने की जरूरत नहीं है, बस इस बात को लेकर जानबूझकर रहें कि कौन से कंपोनेंट्स उन्हें यूज़ करते हैं। अगर कोई हुक डेटा-फेचिंग कॉल को रैप करता है, तो इस पर विचार करें कि क्या वह डेटा इसके बजाय एक सर्वर कंपोनेंट में फेच किया जा सकता है और प्रॉप्स के रूप में पास किया जा सकता है। अक्सर ऐसा हो सकता है, और आप सरल कोड के साथ खत्म होते हैं।

क्या RSC Payload फॉर्मेट के लिए कोई परफॉर्मेंस कॉस्ट है?

हो सकता है, विशेषकर अगर आप पेलोड के माध्यम से बड़ी मात्रा में डेटा पास कर रहे हैं। RSC Payload वही नहीं है जो JSON है, यह एक कस्टम फॉर्मेट है, लेकिन यह अभी भी आपके रिस्पांस में बाइट्स जोड़ता है। ज्यादातर ऐप्स के लिए यह JS बंडल सेविंग्स के मुकाबले नगण्य है। बहुत बड़ी डेटा सेट्स वाले ऐप्स के लिए जो प्रॉप्स के रूप में पास की जा रही हैं, इसे प्रोफाइल करें। यह न मान लें कि RSCs हमेशा वायर पर छोटे होते हैं।

---

RSCs एक वास्तविक आर्किटेक्चरल शिफ्ट हैं, सिर्फ एक नया API नहीं। मानसिक मॉडल को सेटल होने में समय लगता है। उसे वह समय दें। बड़े क्लाइंट प्रोजेक्ट को कमिट करने से पहले app/ डायरेक्टरी के साथ कुछ छोटा सा बनाएँ। मैंने प्रोडक्शन में RSC-आधारित आर्किटेक्चर को शिप करने के लिए खुद पर भरोसा करने से पहले लगभग तीन हफ्ते एक्सपेरिमेंट्स पर बिताए, और मैं 2016 से React लिख रहा हूँ।

जो लोग इसके साथ कुछ सच्चा नहीं बनाते उनसे हाथ हिलाना जारी रहेगा। अब कम से कम आप अंतर को पहचानने के लिए काफी जानते हैं।

← वापस