रिएक्शन विंडो की मदद से, बड़ी सूचियों को वर्चुअल तरीके से सेट करना

बहुत बड़ी टेबल और सूचियों से आपकी साइट की परफ़ॉर्मेंस काफ़ी धीमी हो सकती है. वर्चुअलाइज़ेशन की सुविधा से मदद मिल सकती है!

react-window एक ऐसी लाइब्रेरी है जिसकी मदद से बड़ी सूचियों को बेहतर तरीके से रेंडर किया जा सकता है.

यहां एक सूची का उदाहरण दिया गया है, जिसमें 1,000 पंक्तियां हैं और इन्हें react-window के साथ रेंडर किया जा रहा है. जितनी तेज़ हो सके उतनी तेज़ी से स्क्रीन पर नीचे की ओर स्क्रोल करें.

यह जानकारी आपके काम की क्यों है?

ऐसा हो सकता है कि आपको ऐसी बड़ी टेबल या सूची दिखानी पड़े जिसमें कई पंक्तियां हों. ऐसी सूची में हर आइटम को लोड करने से, परफ़ॉर्मेंस पर काफ़ी असर पड़ सकता है.

सूची को वर्चुअलाइज़ करना या "विंडो करना", सिर्फ़ उस डेटा को रेंडर करने का कॉन्सेप्ट है जो उपयोगकर्ता को दिखता है. शुरुआत में रेंडर किए जाने वाले एलिमेंट की संख्या, पूरी सूची का बहुत छोटा सबसेट होता है. साथ ही, जब उपयोगकर्ता स्क्रोल करता रहता है, तो दिखने वाले कॉन्टेंट की "विंडो" बदले जाती है. इससे सूची की रेंडरिंग और स्क्रोल करने की परफ़ॉर्मेंस, दोनों बेहतर होती हैं.

वर्चुअलाइज़ की गई सूची में कॉन्टेंट की विंडो
वर्चुअलाइज़ की गई सूची में कॉन्टेंट की "विंडो" को मूव करना

"विंडो" से बाहर निकलने वाले डीओएम नोड को रीसाइकल किया जाता है या उपयोगकर्ता सूची को नीचे की ओर स्क्रोल करते ही, उन्हें नए एलिमेंट से तुरंत बदल दिया जाता है. इससे, विंडो के साइज़ के हिसाब से रेंडर किए गए सभी एलिमेंट की संख्या बनी रहती है.

react-window

react-window, तीसरे पक्ष की एक छोटी लाइब्रेरी है. इससे आपके ऐप्लिकेशन में वर्चुअलाइज़ की गई सूचियां बनाना आसान हो जाता है. यह कई बुनियादी एपीआई उपलब्ध कराता है, जिनका इस्तेमाल अलग-अलग तरह की सूचियों और टेबल के लिए किया जा सकता है.

तय साइज़ की सूचियों का इस्तेमाल कब करना चाहिए

अगर आपके पास एक जैसे साइज़ के आइटम की लंबी और एक डाइमेंशन वाली सूची है, तो FixedSizeList कॉम्पोनेंट का इस्तेमाल करें.

import React from 'react';
import { FixedSizeList } from 'react-window';

const items = [...] // some list of items

const Row = ({ index, style }) => (
 
<div style={style}>
     
{/* define the row component using items[index] */}
 
</div>
);

const ListComponent = () => (
  <FixedSizeList
    height={500}
    width={500}
    itemSize={120}
    itemCount={items.length}
  >
    {Row}
  </
FixedSizeList>
);

export default ListComponent;
  • FixedSizeList कॉम्पोनेंट में height, width, और itemSize प्रॉपर्टी का इस्तेमाल किया जा सकता है. इससे, सूची में मौजूद आइटम का साइज़ कंट्रोल किया जा सकता है.
  • लाइनों को रेंडर करने वाला फ़ंक्शन, FixedSizeList को चाइल्ड के तौर पर पास किया जाता है. किसी आइटम की जानकारी, index आर्ग्युमेंट (items[index]) की मदद से ऐक्सेस की जा सकती है.
  • style पैरामीटर को लाइन रेंडर करने के उस तरीके में भी पास किया जाता है जिसे लाइन एलिमेंट से अटैच करना ज़रूरी है. सूची के आइटम को पूरी तरह से तय की गई जगह पर दिखाया जाता है. इसके लिए, इनलाइन में उनकी ऊंचाई और चौड़ाई की वैल्यू असाइन की जाती है. इसकी ज़िम्मेदारी style पैरामीटर की होती है.

इस लेख में पहले दिखाया गया Glitch का उदाहरण, FixedSizeList कॉम्पोनेंट का उदाहरण है.

अलग-अलग साइज़ की सूचियों का इस्तेमाल कब करना चाहिए

अलग-अलग साइज़ वाले आइटम की सूची को रेंडर करने के लिए, VariableSizeList कॉम्पोनेंट का इस्तेमाल करें. यह कॉम्पोनेंट, तय साइज़ की सूची की तरह ही काम करता है. हालांकि, इसमें किसी खास वैल्यू के बजाय itemSize प्रॉपर्टी के लिए फ़ंक्शन की ज़रूरत होती है.

import React from 'react';
import { VariableSizeList } from 'react-window';

const items = [...] // some list of items

const Row = ({ index, style }) => (
 
<div style={style}>
     
{/* define the row component using items[index] */}
 
</div>
);

const getItemSize = index => {
  /
/ return a size for items[index]
}

const ListComponent = () => (
 
<VariableSizeList
    height
={500}
    width
={500}
    itemCount
={items.length}
    itemSize
={getItemSize}
 
>
   
{Row}
 
</VariableSizeList>
);

export default ListComponent;

यहां दिए गए एम्बेड में, इस कॉम्पोनेंट का उदाहरण दिखाया गया है.

इस उदाहरण में, itemSize प्रॉपर्टी में पास किया गया आइटम साइज़ फ़ंक्शन, पंक्ति की ऊंचाई को रैंडम तौर पर तय करता है. हालांकि, किसी असल ऐप्लिकेशन में, हर आइटम के साइज़ तय करने के लिए कोई लॉजिक होना चाहिए. आम तौर पर, इन साइज़ का हिसाब डेटा के आधार पर लगाया जाना चाहिए या एपीआई से पाया जाना चाहिए.

ग्रिड

react-window, कई डाइमेंशन वाली सूचियों या ग्रिड को वर्चुअलाइज़ करने की सुविधा भी देता है. इस संदर्भ में, जब उपयोगकर्ता हॉरिज़ॉन्टल और वर्टिकल तौर पर स्क्रोल करता है, तो दिखने वाले कॉन्टेंट की "विंडो" बदल जाती है.

वर्चुअलाइज़ किए गए ग्रिड में कॉन्टेंट की मूविंग विंडो दो-आयामी होती है
वर्चुअलाइज़ किए गए ग्रिड में कॉन्टेंट की "विंडो" को दो डाइमेंशन में मूव किया जा सकता है

इसी तरह, FixedSizeGrid और VariableSizeGrid, दोनों कॉम्पोनेंट का इस्तेमाल किया जा सकता है. यह इस बात पर निर्भर करता है कि सूची के किसी आइटम का साइज़ अलग-अलग हो सकता है या नहीं.

  • FixedSizeGrid के लिए, एपीआई लगभग एक जैसा है. हालांकि, कॉलम और पंक्तियों, दोनों के लिए ऊंचाई, चौड़ाई, और आइटम की संख्या दिखानी होगी.
  • VariableSizeGrid के लिए, कॉलम की चौड़ाई और पंक्ति की ऊंचाई, दोनों को बदला जा सकता है. इसके लिए, उनके प्रॉप में वैल्यू के बजाय फ़ंक्शन डालें.

वर्चुअलाइज़ किए गए ग्रिड के उदाहरण देखने के लिए, दस्तावेज़ देखें.

स्क्रोल करने पर लेज़ी लोडिंग

कई वेबसाइटें, लंबी सूची में मौजूद आइटम को तब तक लोड और रेंडर नहीं करतीं, जब तक उपयोगकर्ता नीचे की ओर स्क्रोल नहीं कर लेता. इससे वेबसाइट की परफ़ॉर्मेंस बेहतर होती है. इस तकनीक को आम तौर पर "इनफ़ाइनाइट लोडिंग" कहा जाता है. इसमें सूची में नए DOM नोड तब जोड़े जाते हैं, जब उपयोगकर्ता पेज के आखिर में मौजूद किसी तय थ्रेशोल्ड तक स्क्रोल करता है. हालांकि, यह किसी सूची में सभी आइटम को एक साथ लोड करने से बेहतर है, लेकिन अगर उपयोगकर्ता ने उससे ज़्यादा स्क्रोल किया है, तो डीओएम में हज़ारों पंक्ति की एंट्री अपने-आप भर जाती हैं. इससे DOM का साइज़ बहुत ज़्यादा हो सकता है. इससे स्टाइल कैलकुलेशन और DOM म्यूटेशन धीमे होने पर, परफ़ॉर्मेंस पर असर पड़ता है.

इस डायग्राम से, इस बारे में खास जानकारी मिल सकती है:

सामान्य और वर्चुअलाइज़ की गई सूची के बीच स्क्रोल करने में अंतर
रेगुलर और वर्चुअलाइज़ की गई सूची के बीच स्क्रोल करने में अंतर

इस समस्या को हल करने का सबसे अच्छा तरीका यह है कि react-window जैसी लाइब्रेरी का इस्तेमाल जारी रखा जाए, ताकि पेज पर एलिमेंट की छोटी "विंडो" बनी रहे. साथ ही, उपयोगकर्ता के नीचे की ओर स्क्रोल करने पर, नई एंट्री को भी धीरे-धीरे लोड किया जा सके. react-window-infinite-loader नाम का एक अलग पैकेज, react-window के साथ ऐसा करने की सुविधा देता है.

नीचे दिए गए कोड पर ध्यान दें. इसमें, पैरंट App कॉम्पोनेंट में मैनेज की जाने वाली स्थिति का उदाहरण दिया गया है.

import React, { Component } from 'react';

import ListComponent from './ListComponent';

class App extends Component {
  constructor
(props) {
   
super(props);
   
this.state = {
      items
: [], // instantiate initial list here
      moreItemsLoading
: false,
      hasNextPage
: true
   
};

   
this.loadMore = this.loadMore.bind(this);
 
}

  loadMore
() {
   
// method to fetch newer entries for the list
 
}

  render
() {
   
const { items, moreItemsLoading, hasNextPage } = this.state;

   
return (
     
<ListComponent
        items
={items}
        moreItemsLoading
={moreItemsLoading}
        loadMore
={this.loadMore}
        hasNextPage
={hasNextPage}
     
/>
   
);
 
}
}

export default App;

loadMore तरीके को चाइल्ड ListComponent में पास किया जाता है, जिसमें अनलिमिटेड लोडर की सूची होती है. यह ज़रूरी है, क्योंकि जब उपयोगकर्ता किसी तय बिंदु से आगे स्क्रोल करता है, तो इनफ़ाइनाइट लोडर को ज़्यादा आइटम लोड करने के लिए, कॉलबैक ट्रिगर करना होता है.

सूची को रेंडर करने वाला ListComponent इस तरह दिख सकता है:

import React from 'react';
import { FixedSizeList } from 'react-window';
import InfiniteLoader from "react-window-infinite-loader";

const ListComponent = ({ items, moreItemsLoading, loadMore, hasNextPage }) => {
 
const Row = ({ index, style }) => (
     
{/* define the row component using items[index] */}
 
);

 
const itemCount = hasNextPage ? items.length + 1 : items.length;

 
return (
   
<InfiniteLoader
      isItemLoaded
={index => index < items.length}
      itemCount
={itemCount}
      loadMoreItems
={loadMore}
   
>
     
{({ onItemsRendered, ref }) => (
       
<FixedSizeList
          height
={500}
          width
={500}
          itemCount
={itemCount}
          itemSize
={120}
          onItemsRendered
={onItemsRendered}
         
ref={ref}
       
>
         
{Row}
       
</FixedSizeList>
      )}
  </
InfiniteLoader>
 
)
};

export default ListComponent;

यहां, FixedSizeList कॉम्पोनेंट को InfiniteLoader के अंदर रैप किया गया है. लोडर के लिए असाइन किए गए प्रॉप ये हैं:

  • isItemLoaded: यह तरीका यह जांचता है कि कोई आइटम लोड हुआ है या नहीं
  • itemCount: सूची में मौजूद आइटम की संख्या (या उम्मीद के मुताबिक)
  • loadMoreItems: ऐसा कॉलबैक जो सूची के लिए अतिरिक्त डेटा दिखाने वाला प्रॉमिस दिखाता है

रेंडर प्रॉप का इस्तेमाल, एक ऐसा फ़ंक्शन दिखाने के लिए किया जाता है जिसका इस्तेमाल सूची कॉम्पोनेंट, रेंडर करने के लिए करता है. onItemsRendered और ref, दोनों एट्रिब्यूट ऐसे एट्रिब्यूट हैं जिन्हें सबमिट करना ज़रूरी है.

यहां एक उदाहरण दिया गया है, जिसमें दिखाया गया है कि वर्चुअलाइज़ की गई सूची के साथ अनलिमिटेड लोडिंग कैसे काम कर सकती है.

सूची को नीचे की ओर स्क्रोल करने पर, आपको पहले जैसा ही अनुभव मिल सकता है. हालांकि, अब सूची के आखिर तक स्क्रोल करने पर, रैंडम उपयोगकर्ता एपीआई से 10 उपयोगकर्ताओं को वापस पाने का अनुरोध किया जाता है. यह सब तब किया जाता है, जब एक बार में सिर्फ़ नतीजों की एक "विंडो" रेंडर की जाती है.

किसी आइटम के index की जांच करके, उस आइटम के लिए लोड होने की अलग-अलग स्थिति देखी जा सकती है. यह इस बात पर निर्भर करता है कि नई एंट्री के लिए अनुरोध किया गया है या नहीं और आइटम अब भी लोड हो रहा है या नहीं.

उदाहरण के लिए:

const Row = ({ index, style }) => {
 
const itemLoading = index === items.length;

 
if (itemLoading) {
     
// return loading state
 
} else {
     
// return item
 
}
};

ओवरस्कैनिंग

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

वर्चुअलाइज़ की गई सूचियों के उपयोगकर्ता अनुभव को बेहतर बनाने के लिए, react-window की मदद से overscanCount प्रॉपर्टी के साथ आइटम को ओवरस्कैन किया जा सकता है. इससे, यह तय किया जा सकता है कि दिखने वाली "विंडो" के बाहर, कितने आइटम हमेशा रेंडर किए जाएं.

<FixedSizeList
 
//...
 
overscanCount={4}
>
  {...}
</FixedSizeList>

overscanCount, FixedSizeList और VariableSizeList दोनों कॉम्पोनेंट के लिए काम करता है. इसकी डिफ़ॉल्ट वैल्यू 1 होती है. सूची के साइज़ और हर आइटम के साइज़ के आधार पर, एक से ज़्यादा एंट्री को ओवरस्कैन करने से, उपयोगकर्ता के स्क्रोल करने पर खाली जगह के फ़्लैश होने से रोका जा सकता है. हालांकि, बहुत ज़्यादा एंट्री को स्कैन करने से परफ़ॉर्मेंस पर बुरा असर पड़ सकता है. वर्चुअलाइज़ की गई सूची का इस्तेमाल करने का मकसद, उन एंट्री की संख्या को कम करना है जिन्हें उपयोगकर्ता किसी भी समय देख सकता है. इसलिए, ज़रूरत से ज़्यादा स्कैन किए गए आइटम की संख्या को कम से कम रखें.

FixedSizeGrid और VariableSizeGrid के लिए, overscanColumnsCount और overscanRowsCount प्रॉपर्टी का इस्तेमाल करके, क्रमशः ओवरस्कैन करने के लिए कॉलम और पंक्तियों की संख्या को कंट्रोल करें.

नतीजा

अगर आपको यह नहीं पता कि अपने ऐप्लिकेशन में सूचियों और टेबल को वर्चुअलाइज़ करने की प्रोसेस कहां से शुरू करनी है, तो यह तरीका अपनाएं:

  1. रेंडरिंग और स्क्रोलिंग की परफ़ॉर्मेंस मेज़र करें. इस लेख में बताया गया है कि Chrome DevTools में एफ़पीएस (फ़्रेम प्रति सेकंड) मेटर का इस्तेमाल करके, यह कैसे पता लगाया जा सकता है कि किसी सूची में आइटम कितनी तेज़ी से रेंडर होते हैं.
  2. परफ़ॉर्मेंस पर असर डालने वाली लंबी सूचियों या ग्रिड के लिए react-window शामिल करें.
  3. अगर react-window में कुछ सुविधाएं काम नहीं करती हैं, तो react-virtualized का इस्तेमाल करें. हालांकि, अगर आपके पास खुद से यह सुविधा जोड़ने का विकल्प नहीं है, तो ऐसा करें.
  4. अगर आपको उपयोगकर्ता के स्क्रोल करने पर, आइटम को धीरे-धीरे लोड करना है, तो अपनी वर्चुअलाइज़ की गई सूची को react-window-infinite-loader के साथ रैप करें.
  5. खाली कॉन्टेंट को फ़्लैश होने से रोकने के लिए, अपनी सूचियों के लिए overscanCount प्रॉपर्टी और अपने ग्रिड के लिए overscanColumnsCount और overscanRowsCount प्रॉपर्टी का इस्तेमाल करें. बहुत ज़्यादा एंट्री को ओवरस्कैन न करें, क्योंकि इससे परफ़ॉर्मेंस पर बुरा असर पड़ेगा.