রিঅ্যাক্ট-উইন্ডো দিয়ে বড় তালিকা ভার্চুয়ালাইজ করুন

অত্যন্ত বড় টেবিল এবং তালিকা আপনার সাইটের পারফরম্যান্স উল্লেখযোগ্যভাবে কমিয়ে দিতে পারে। ভার্চুয়ালাইজেশন এক্ষেত্রে সাহায্য করতে পারে!

react-window একটি লাইব্রেরি যা দিয়ে বড় তালিকা দক্ষতার সাথে রেন্ডার করা যায়।

এটি কেন দরকারি?

কখনও কখনও আপনাকে অনেকগুলো সারি সম্বলিত একটি বড় টেবিল বা তালিকা প্রদর্শন করতে হতে পারে। এই ধরনের তালিকার প্রতিটি আইটেম লোড করা হলে তা পারফরম্যান্সকে উল্লেখযোগ্যভাবে প্রভাবিত করতে পারে।

লিস্ট ভার্চুয়ালাইজেশন বা ‘উইন্ডোয়িং’ হলো ব্যবহারকারীর কাছে কেবল দৃশ্যমান অংশটুকুই রেন্ডার করার একটি ধারণা। প্রথমে যে সংখ্যক উপাদান রেন্ডার করা হয়, তা সম্পূর্ণ তালিকার একটি খুব ছোট অংশ এবং ব্যবহারকারী স্ক্রল করতে থাকলে দৃশ্যমান অংশের ‘উইন্ডো’টি সরে যায় । এটি তালিকার রেন্ডারিং এবং স্ক্রলিং উভয় পারফরম্যান্সই উন্নত করে।

ভার্চুয়ালাইজড তালিকার বিষয়বস্তুর উইন্ডো
একটি ভার্চুয়ালাইজড তালিকায় বিষয়বস্তুর "উইন্ডো" সরানো

যেসব DOM নোড "উইন্ডো" থেকে বেরিয়ে যায়, ব্যবহারকারী যখন তালিকাটি স্ক্রল করে নিচে নামেন, তখন সেগুলোকে রিসাইকেল করা হয় বা তাৎক্ষণিকভাবে নতুন এলিমেন্ট দিয়ে প্রতিস্থাপন করা হয়। এর ফলে রেন্ডার করা সমস্ত এলিমেন্টের সংখ্যা উইন্ডোর আকারের সাথে সামঞ্জস্যপূর্ণ থাকে।

রিঅ্যাক্ট-উইন্ডো

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 প্রপে পাস করা `item size` ফাংশনটি সারির উচ্চতাগুলোকে এলোমেলোভাবে নির্ধারণ করে। কিন্তু একটি বাস্তব অ্যাপ্লিকেশনে, প্রতিটি আইটেমের আকার নির্ধারণ করার জন্য প্রকৃত লজিক থাকা উচিত। আদর্শগতভাবে, এই আকারগুলো ডেটার উপর ভিত্তি করে গণনা করা উচিত অথবা কোনো এপিআই (API) থেকে সংগ্রহ করা উচিত।

গ্রিড

react-window বহুমাত্রিক তালিকা বা গ্রিড ভার্চুয়ালাইজ করার সুবিধাও প্রদান করে। এক্ষেত্রে, ব্যবহারকারী যখন আনুভূমিকভাবে এবং উল্লম্বভাবে স্ক্রল করেন, তখন দৃশ্যমান কন্টেন্টের 'উইন্ডো' পরিবর্তিত হয়।

ভার্চুয়ালাইজড গ্রিডে থাকা কন্টেন্টের চলমান উইন্ডোটি দ্বিমাত্রিক।
ভার্চুয়ালাইজড গ্রিডে কন্টেন্টের চলমান 'উইন্ডো'টি দ্বিমাত্রিক।

একইভাবে, তালিকার নির্দিষ্ট আইটেমগুলোর আকার পরিবর্তনশীল হবে কি না, তার ওপর নির্ভর করে FixedSizeGrid এবং VariableSizeGrid উভয় কম্পোনেন্টই ব্যবহার করা যেতে পারে।

  • FixedSizeGrid এর ক্ষেত্রে API প্রায় একই রকম, তবে এক্ষেত্রে কলাম এবং সারি উভয়ের জন্যই উচ্চতা, প্রস্থ এবং আইটেমের সংখ্যা উল্লেখ করতে হয়।
  • VariableSizeGrid এর ক্ষেত্রে, কলামের প্রস্থ এবং সারির উচ্চতা উভয়ই তাদের নিজ নিজ প্রপসে মানের পরিবর্তে ফাংশন পাস করে পরিবর্তন করা যায়।

ভার্চুয়ালাইজড গ্রিডের উদাহরণ দেখতে ডকুমেন্টেশনটি দেখুন।

স্ক্রোল করার সময় লেজি লোডিং

অনেক ওয়েবসাইট একটি দীর্ঘ তালিকার আইটেমগুলো ব্যবহারকারী স্ক্রল করে নিচে নামা পর্যন্ত অপেক্ষা করে পারফরম্যান্স উন্নত করে। এই কৌশলটি, যা সাধারণত "ইনফিনিট লোডিং" নামে পরিচিত, ব্যবহারকারী তালিকার শেষের দিকে একটি নির্দিষ্ট সীমা অতিক্রম করে স্ক্রল করার সাথে সাথে তালিকায় নতুন DOM নোড যুক্ত করে। যদিও এটি তালিকার সমস্ত আইটেম একবারে লোড করার চেয়ে ভালো, তবুও ব্যবহারকারী যদি একটি নির্দিষ্ট সংখ্যক আইটেম স্ক্রল করে ফেলে, তবে এটি 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 উভয় অ্যাট্রিবিউটই পাস করতে হয়।

তালিকাটি স্ক্রল করে নিচে নামার অনুভূতি একই রকম মনে হতে পারে, কিন্তু এখন প্রতিবার তালিকার শেষের দিকে স্ক্রল করার সময় একটি র‍্যান্ডম ইউজার এপিআই (API) থেকে ১০ জন ব্যবহারকারীকে আনার জন্য একটি অনুরোধ পাঠানো হয়। এই পুরো কাজটি একবারে ফলাফলের কেবল একটি "উইন্ডো" রেন্ডার করার মাধ্যমেই করা হয়।

কোনো নির্দিষ্ট আইটেমের 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 উভয় কম্পোনেন্টের জন্যই কাজ করে এবং এর ডিফল্ট মান হলো ১। একটি লিস্ট কতটা বড় এবং প্রতিটি আইটেমের আকারের উপর নির্ভর করে, একাধিক এন্ট্রি ওভারস্ক্যান করলে ব্যবহারকারী স্ক্রল করার সময় খালি জায়গার একটি লক্ষণীয় ঝলক প্রতিরোধ করা যেতে পারে। তবে, খুব বেশি এন্ট্রি ওভারস্ক্যান করলে তা পারফরম্যান্সের উপর নেতিবাচক প্রভাব ফেলতে পারে। একটি ভার্চুয়ালাইজড লিস্ট ব্যবহারের মূল উদ্দেশ্যই হলো এন্ট্রির সংখ্যা কমিয়ে ব্যবহারকারী যেকোনো মুহূর্তে যা দেখতে পায় তার মধ্যে সীমাবদ্ধ রাখা, তাই ওভারস্ক্যান করা আইটেমের সংখ্যা যথাসম্ভব কম রাখার চেষ্টা করুন।

FixedSizeGrid এবং VariableSizeGrid ক্ষেত্রে, যথাক্রমে কতগুলো কলাম এবং সারি ওভারস্ক্যান করা হবে তা নিয়ন্ত্রণ করতে overscanColumnsCount এবং overscanRowsCount প্রপার্টিগুলো ব্যবহার করুন।

উপসংহার

আপনার অ্যাপ্লিকেশনে লিস্ট এবং টেবিল ভার্চুয়ালাইজ করা কোথা থেকে শুরু করবেন সে বিষয়ে আপনি যদি অনিশ্চিত থাকেন, তাহলে এই ধাপগুলো অনুসরণ করুন:

  1. রেন্ডারিং এবং স্ক্রলিং পারফরম্যান্স পরিমাপ করুন। এই নিবন্ধে দেখানো হয়েছে, কীভাবে ক্রোম ডেভটুলস-এর এফপিএস মিটার ব্যবহার করে একটি তালিকার আইটেমগুলো কতটা দক্ষতার সাথে রেন্ডার হচ্ছে তা জানা যায়।
  2. যেসব দীর্ঘ তালিকা বা গ্রিড পারফরম্যান্সকে প্রভাবিত করছে, সেগুলোর জন্য react-window অন্তর্ভুক্ত করুন।
  3. react-window এ কিছু নির্দিষ্ট ফিচার সমর্থিত না হলে এবং আপনি নিজে সেই কার্যকারিতা যোগ করতে না পারলে, react-virtualized ব্যবহার করার কথা বিবেচনা করতে পারেন।
  4. ব্যবহারকারী স্ক্রোল করার সাথে সাথে আইটেমগুলো লেজি লোড করার প্রয়োজন হলে, আপনার ভার্চুয়ালাইজড লিস্টটিকে react-window-infinite-loader দিয়ে র‍্যাপ করুন।
  5. হঠাৎ করে খালি কন্টেন্ট চলে আসা রোধ করতে আপনার লিস্টের জন্য overscanCount প্রপার্টি এবং গ্রিডের জন্য overscanColumnsCountoverscanRowsCount প্রপার্টি ব্যবহার করুন। খুব বেশি এন্ট্রি ওভারস্ক্যান করবেন না, কারণ এটি পারফরম্যান্সকে নেতিবাচকভাবে প্রভাবিত করবে।