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

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

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 पैरामीटर ज़िम्मेदार होता है.

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

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 में हज़ारों लाइनें जोड़ देता है. इससे डीओएम का साइज़ बहुत बड़ा हो सकता है. इसकी वजह से, स्टाइल कैलकुलेशन और डीओएम म्यूटेशन की प्रोसेस धीमी हो जाती है और परफ़ॉर्मेंस पर असर पड़ता है.

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

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

इस समस्या को हल करने का सबसे अच्छा तरीका यह है कि पेज पर एलिमेंट की छोटी "विंडो" बनाए रखने के लिए, 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 प्रॉपर्टी का इस्तेमाल करें, ताकि खाली कॉन्टेंट न दिखे. बहुत ज़्यादा एंट्री स्कैन न करें, क्योंकि इससे परफ़ॉर्मेंस पर बुरा असर पड़ेगा.