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

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

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

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

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

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

यहां दिए गए कोड पर ध्यान दें. इसमें, पैरंट 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, दोनों ऐसे एट्रिब्यूट हैं जिनकी वैल्यू पास करनी होती है.

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

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