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

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

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

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

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

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

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

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

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

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