useTransition

useTransition হলো একটি React হুক যা আপনাকে UI ব্লক না করেই স্টেট আপডেট করতে দেয়।

const [isPending, startTransition] = useTransition()

রেফারেন্স

useTransition()

আপনার কম্পোনেন্টের একেবারে উপরের স্তরে useTransition কল করুন যাতে কিছু state আপডেটকে ট্রানজিশন হিসাবে চিহ্নিত করা যায়।

import { useTransition } from 'react';

function TabContainer() {
const [isPending, startTransition] = useTransition();
// ...
}

নীচে আরো উদাহরণ দেখুন।

প্যারামিটারস

useTransition কোনো প্যারামিটার নেয় না।

রিটার্নস

useTransition একটি অ্যারে রিটার্ন করে যাতে ঠিক দুটি আইটেম থাকে:

১. isPending ফ্ল্যাগ যা আপনাকে জানায় যে একটি পেন্ডিং ট্রানজিশন আছে। ২. startTransition ফাংশনটি যা আপনাকে একটি স্টেট আপডেটকে ট্রানজিশন হিসাবে চিহ্নিত করতে দেয়।


startTransition ফাংশন

useTransition দ্বারা রিটার্ন করা startTransition ফাংশনটি আপনাকে একটি state আপডেটকে ট্রানজিশন হিসাবে চিহ্নিত করতে দেয়।

function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');

function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}

প্যারামিটারসমূহ

রিটার্নস

startTransition কিছু রিটার্ন করে না।

সতর্কতা

  • useTransition হল একটি হুক, তাই এটি কেবল কম্পোনেন্ট বা কাস্টম হুকের মধ্যে কল করা যেতে পারে। যদি অন্য কোনো স্থানে (যেমন, একটি ডেটা লাইব্রেরি থেকে) ট্রানজিশন শুরু করার প্রয়োজন হয়, তাহলে স্বতন্ত্র startTransition কল করুন।

  • যদি আপনি একটি স্টেটের সেট ফাংশনে অ্যাক্সেস পেয়ে থাকেন তবে আপনি একটি ট্রানজিশনে আপডেট wrap করতে পারেন। কোনো প্রপ বা কাস্টম হুক ভ্যালুর রেসপন্সে ট্রানজিশন শুরু করতে চাইলে, useDeferredValue ব্যবহার করার চেষ্টা করুন।

  • startTransition এ আপনি যে ফাংশন পাস করবেন তা অবশ্যই একই সময়ে হতে হবে। React এই ফাংশনটি তাৎক্ষণিকভাবে চালায়, এটি চালানোর সময় ঘটে যাওয়া সমস্ত state আপডেটকে ট্রানজিশন হিসাবে চিহ্নিত করে। যদি আপনি পরে আরো state আপডেট করার চেষ্টা করেন (উদাহরণস্বরূপ, একটি timeout এ), তবে সেগুলি ট্রানজিশন হিসাবে চিহ্নিত হবে না।

  • একটি state আপডেট যদি ট্রানজিশন হিসাবে চিহ্নিত হয়, তাহলে অন্যান্য state আপডেট দ্বারা তা বাধাগ্রস্ত হবে। উদাহরণস্বরূপ, যদি আপনি একটি চার্ট কম্পোনেন্টে একটি ট্রানজিশনের মধ্যে আপডেট করেন, কিন্তু তারপর চার্টটি পুনরায় রেন্ডার হওয়ার মাঝখানে একটি input-এ টাইপ শুরু করেন, React ইনপুট আপডেট সম্পর্কিত কাজ সম্পন্ন করার পরে চার্ট কম্পোনেন্টে রেন্ডারিং কাজটি পুনরায় শুরু করবে।

  • Transition আপডেটগুলি টেক্সট ইনপুটগুলি নিয়ন্ত্রণের জন্য ব্যবহৃত হতে পারে না।

  • যদি একাধিক চলমান transitions থাকে, React বর্তমানে তাদেরকে একসাথে ব্যাচ করে। এটি একটি সীমাবদ্ধতা যা সম্ভবত ভবিষ্যতের কোনো রিলিজে সরানো হবে।


ব্যবহারবিধি

একটি state আপডেটকে নন-ব্লকিং ট্রানজিশন হিসাবে চিহ্নিত করা

কম্পোনেন্টের একেবারে উপরে useTransition কল করুন যাতে state আপডেটগুলি কোনো প্রকার বাধা ছাড়াই ট্রানজিশন হিসাবে চিহ্নিত করা যায়।

import { useState, useTransition } from 'react';

function TabContainer() {
const [isPending, startTransition] = useTransition();
// ...
}

useTransition ঠিক দুটি আইটেম সহ একটি array রিটার্ন করে:

১. isPending ফ্ল্যাগ যা আপনাকে জানায় যে একটি পেন্ডিং ট্রানজিশন রয়েছে। ২. startTransition ফাংশন যা আপনাকে একটি state আপডেটকে ট্রানজিশন হিসাবে চিহ্নিত করতে দেয়।

আপনি তখন একটি state আপডেটকে এইরকম একটি ট্রানজিশন হিসাবে চিহ্নিত করতে পারেন:

function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');

function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}

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

একটি ট্রানজিশনের সাথে, আপনার UI রি-রেন্ডারের মাঝখানে রেসপন্সিভ থাকে। উদাহরণস্বরূপ, যদি ব্যবহারকারী একটি ট্যাবে ক্লিক করে কিন্তু তারপর তাদের মন পরিবর্তন করে এবং অন্য ট্যাবে ক্লিক করে, তারা প্রথম রি-রেন্ডার শেষ হওয়ার জন্য অপেক্ষা না করে এটি করতে পারে।

useTransition এবং রেগুলার state আপডেটের মধ্যে পার্থক্য

উদাহরণ 1 / 2:
ট্রানজিশনের মাধ্যমে বর্তমান ট্যাবটি আপডেট করা হচ্ছে

এই উদাহরণে, “Posts” ট্যাবটি কৃত্রিমভাবে ধীর করা হয়েছে যাতে এটি রেন্ডার করতে কমপক্ষে এক সেকেন্ড সময় নেয়।

“Posts” এ ক্লিক করুন এবং তারপর তাৎক্ষণিকভাবে “Contact” এ ক্লিক করুন। লক্ষ্য করুন যে এটি “Posts” এর ধীরগতির রেন্ডারকে বাধা দেয়। “Contact” ট্যাবটি তাৎক্ষণিকভাবে দেখায়। কারণ এই state আপডেটটি একটি transition হিসেবে চিহ্নিত হয়েছে, একটি ধীরগতির রি-রেন্ডার ইউজার ইন্টারফেসকে স্থির রাখতে পারেনি।

import { useState, useTransition } from 'react';
import TabButton from './TabButton.js';
import AboutTab from './AboutTab.js';
import PostsTab from './PostsTab.js';
import ContactTab from './ContactTab.js';

export default function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }

  return (
    <>
      <TabButton
        isActive={tab === 'about'}
        onClick={() => selectTab('about')}
      >
        About
      </TabButton>
      <TabButton
        isActive={tab === 'posts'}
        onClick={() => selectTab('posts')}
      >
        Posts (slow)
      </TabButton>
      <TabButton
        isActive={tab === 'contact'}
        onClick={() => selectTab('contact')}
      >
        Contact
      </TabButton>
      <hr />
      {tab === 'about' && <AboutTab />}
      {tab === 'posts' && <PostsTab />}
      {tab === 'contact' && <ContactTab />}
    </>
  );
}


ট্রানজিশনে প্যারেন্ট কম্পোনেন্ট আপডেট করা

আপনি useTransition কল থেকে একটি প্যারেন্ট কম্পোনেন্টের state আপডেট করতে পারেন। উদাহরণস্বরূপ, এই TabButton কম্পোনেন্টটি এর onClick লজিককে একটি ট্রানজিশনে রাখে:

export default function TabButton({ children, isActive, onClick }) {
const [isPending, startTransition] = useTransition();
if (isActive) {
return <b>{children}</b>
}
return (
<button onClick={() => {
startTransition(() => {
onClick();
});
}}>
{children}
</button>
);
}

কারণ প্যারেন্ট কম্পোনেন্ট তার state আপডেট করে onClick ইভেন্ট হ্যান্ডলারের মধ্যে, সেই state আপডেটটি একটি ট্রানজিশন হিসাবে বিবেচিত হয়। এই কারণে, আগের উদাহরণের মতো, আপনি “Posts” এ ক্লিক করতে পারেন এবং তারপর অবিলম্বে “Contact” এ ক্লিক করতে পারেন। নির্বাচিত ট্যাব আপডেট করা একটি ট্রানজিশন হিসাবে বিবেচিত হয়, তাই এটি ইউজার ইন্টারঅ্যাকশনগুলি বাধা দেয় না।

import { useTransition } from 'react';

export default function TabButton({ children, isActive, onClick }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        onClick();
      });
    }}>
      {children}
    </button>
  );
}


ট্রানজিশনের সময় পেন্ডিং ভিজ্যুয়াল state প্রদর্শন করা

আপনি useTransition হতে রিটার্ন আসা isPending বুলিয়ান মান ব্যবহার করে ব্যবহারকারীকে জানাতে পারেন যে একটি ট্রানজিশন চলছে। উদাহরণস্বরূপ, ট্যাব বোতামটি একটি বিশেষ “pending” ভিজ্যুয়াল state থাকতে পারে:

function TabButton({ children, isActive, onClick }) {
const [isPending, startTransition] = useTransition();
// ...
if (isPending) {
return <b className="pending">{children}</b>;
}
// ...

লক্ষ্য করুন যে “Posts” ক্লিক করা এখন কিভাবে আরও প্রতিক্রিয়াশীল মনে হয় কারণ ট্যাব বাটনটি নিজেই তাৎক্ষণিকভাবে আপডেট হয়:

import { useTransition } from 'react';

export default function TabButton({ children, isActive, onClick }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  if (isPending) {
    return <b className="pending">{children}</b>;
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        onClick();
      });
    }}>
      {children}
    </button>
  );
}


অনাকাঙ্ক্ষিত লোডিং ইন্ডিকেটরগুলি প্রতিরোধ করা

এই উদাহরণে, PostsTab কম্পোনেন্টটি একটি Suspense-enabled ডেটা সোর্স ব্যবহার করে কিছু ডেটা আনয়ন করে। যখন আপনি “Posts” ট্যাবে ক্লিক করেন, তখন PostsTab কম্পোনেন্টটি সাসপেন্ড হয়, যা কাছাকাছি লোডিং ফলব্যাক প্রদর্শন করে:

import { Suspense, useState } from 'react';
import TabButton from './TabButton.js';
import AboutTab from './AboutTab.js';
import PostsTab from './PostsTab.js';
import ContactTab from './ContactTab.js';

export default function TabContainer() {
  const [tab, setTab] = useState('about');
  return (
    <Suspense fallback={<h1>🌀 Loading...</h1>}>
      <TabButton
        isActive={tab === 'about'}
        onClick={() => setTab('about')}
      >
        About
      </TabButton>
      <TabButton
        isActive={tab === 'posts'}
        onClick={() => setTab('posts')}
      >
        Posts
      </TabButton>
      <TabButton
        isActive={tab === 'contact'}
        onClick={() => setTab('contact')}
      >
        Contact
      </TabButton>
      <hr />
      {tab === 'about' && <AboutTab />}
      {tab === 'posts' && <PostsTab />}
      {tab === 'contact' && <ContactTab />}
    </Suspense>
  );
}

পুরো ট্যাব কন্টেইনার লুকিয়ে একটি লোডিং ইন্ডিকেটর দেখানো ব্যবহারকারীর জন্য অস্বস্তিকর অভিজ্ঞতা তৈরি করে। যদি আপনি TabButtonuseTransition যোগ করেন, তাহলে আপনি ট্যাব বোতামে পেন্ডিং state দেখানোর জন্য নির্দেশ করতে পারেন।

লক্ষ্য করুন যে “Posts” ক্লিক করলে আর পুরো ট্যাব কন্টেইনার একটি স্পিনার দিয়ে প্রতিস্থাপিত হয় না:

import { useTransition } from 'react';

export default function TabButton({ children, isActive, onClick }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  if (isPending) {
    return <b className="pending">{children}</b>;
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        onClick();
      });
    }}>
      {children}
    </button>
  );
}

Suspense এর সাথে transitions ব্যবহার সম্পর্কে আরও পড়ুন।

খেয়াল করুন

ট্রানজিশনগুলি কেবল ইতিমধ্যে প্রকাশিত কন্টেন্ট (যেমন ট্যাব কন্টেইনার) লুকানো এড়াতে যথেষ্ট দীর্ঘ সময় “অপেক্ষা” করবে। যদি Posts ট্যাবের মধ্যে একটি নেস্টেড <Suspense> সীমানা থাকত, তবে ট্রানজিশনটি এর জন্য “অপেক্ষা” করত না।


Suspense-সংবলিত রাউটার তৈরি করা

যদি আপনি একটি React ফ্রেমওয়ার্ক অথবা রাউটার তৈরি করেন, আমরা পরামর্শ দিই আপনি আপনার নেভিগেশনগুলিকে ট্রানজিশন হিসেবে চিহ্নিত করুন।

function Router() {
const [page, setPage] = useState('/');
const [isPending, startTransition] = useTransition();

function navigate(url) {
startTransition(() => {
setPage(url);
});
}
// ...

এটি দুটি কারণে পরামর্শ দেয়া হয়:

এখানে নেভিগেশনের জন্য ট্রানজিশন ব্যবহার করে একটি খুব সহজ রাউটার উদাহরণ দেওয়া হল।

import { Suspense, useState, useTransition } from 'react';
import IndexPage from './IndexPage.js';
import ArtistPage from './ArtistPage.js';
import Layout from './Layout.js';

export default function App() {
  return (
    <Suspense fallback={<BigSpinner />}>
      <Router />
    </Suspense>
  );
}

function Router() {
  const [page, setPage] = useState('/');
  const [isPending, startTransition] = useTransition();

  function navigate(url) {
    startTransition(() => {
      setPage(url);
    });
  }

  let content;
  if (page === '/') {
    content = (
      <IndexPage navigate={navigate} />
    );
  } else if (page === '/the-beatles') {
    content = (
      <ArtistPage
        artist={{
          id: 'the-beatles',
          name: 'The Beatles',
        }}
      />
    );
  }
  return (
    <Layout isPending={isPending}>
      {content}
    </Layout>
  );
}

function BigSpinner() {
  return <h2>🌀 Loading...</h2>;
}

খেয়াল করুন

Suspense-enabled রাউটারগুলি সাধারণত নেভিগেশন আপডেটগুলিকে ডিফল্টভাবে transitions এ মোড়ানোর আশা করা হয়।


ব্যবহারকারীদের কাছে ত্রুটি সীমানা ব্যবহার করে ত্রুটি প্রদর্শন করা

Canary

useTransition জন্য এরর বাউন্ডারি বর্তমানে কেবল React-এর canary এবং পরীক্ষামূলক চ্যানেলগুলিতে পাওয়া যায়। এখানে React-এর রিলিজ চ্যানেলগুলি সম্পর্কে আরও জানুন।

যদি startTransition এ পাস করা কোনো ফাংশন কোনো ত্রুটি দেখায়, তাহলে আপনি আপনার ব্যবহারকারীকে সেই ত্রুটির বার্তাটি প্রদর্শন করতে পারেন একটি এরর বাউন্ডারির মাধ্যমে। এরর বাউন্ডারি ব্যবহার করতে, যে কম্পোনেন্টে আপনি useTransition কল করছেন তাকে একটি এরর বাউন্ডারির মধ্যে মোড়ান। একবার startTransition এ পাস করা ফাংশনে ত্রুটি ঘটলে, এরর বাউন্ডারির জন্য নির্ধারিত ফলব্যাক প্রদর্শিত হবে।

import { useTransition } from "react";
import { ErrorBoundary } from "react-error-boundary";

export function AddCommentContainer() {
  return (
    <ErrorBoundary fallback={<p>⚠️Something went wrong</p>}>
      <AddCommentButton />
    </ErrorBoundary>
  );
}

function addComment(comment) {
  // For demonstration purposes to show Error Boundary
  if (comment == null) {
    throw new Error("Example Error: An error thrown to trigger error boundary");
  }
}

function AddCommentButton() {
  const [pending, startTransition] = useTransition();

  return (
    <button
      disabled={pending}
      onClick={() => {
        startTransition(() => {
          // Intentionally not passing a comment
          // so error gets thrown
          addComment();
        });
      }}
    >
      Add comment
    </button>
  );
}


সমস্যা সমাধান

একটি ট্রানজিশনে ইনপুট আপডেট করা কাজ করে না

আপনি এমন একটি state ভেরিয়েবলের জন্য ট্রানজিশন ব্যবহার করতে পারবেন না যা একটি ইনপুট নিয়ন্ত্রণ করে:

const [text, setText] = useState('');
// ...
function handleChange(e) {
// ❌ Can't use Transitions for controlled input state
startTransition(() => {
setText(e.target.value);
});
}
// ...
return <input value={text} onChange={handleChange} />;

এর কারণ হল ট্রানজিশনগুলি নন-ব্লকিং, কিন্তু change ইভেন্টের রেসপন্সে ইনপুট আপডেট করা অবশ্যই একই সময়ে হওয়া উচিত। যদি আপনি টাইপ করার সময় একটি ট্রানজিশন চালাতে চান, তাহলে আপনার দুটি বিকল্প উপায় রয়েছে:

১. আপনি দুটি আলাদা state ভেরিয়েবল ঘোষণা করতে পারেন: একটি ইনপুট state এর জন্য (যা সর্বদা একই সময়ে আপডেট হয়), এবং একটি যা আপনি ট্রানজিশনে আপডেট করবেন। এটি আপনাকে একই সময়ে state ব্যবহার করে ইনপুট নিয়ন্ত্রণ করতে দেয়, এবং বাকি রেন্ডারিং লজিকে ট্রানজিশন state ভেরিয়েবল (যা ইনপুটের পিছনে “বিলম্বিত” হবে) পাস করতে দেয়। ২. বিকল্প হিসেবে, আপনি একটি state ভেরিয়েবল রাখতে পারেন, এবং useDeferredValue যোগ করতে পারেন যা বাস্তব মানের পিছনে “বিলম্বিত” হবে। এটি নতুন মানের সাথে “মেলে যাওয়ার” জন্য নন-ব্লকিং রি-রেন্ডারগুলি স্বয়ংক্রিয়ভাবে ট্রিগার করবে।


React আমার state আপডেটকে ট্রানজিশন হিসেবে গ্রহণ করে না

যখন আপনি একটি state আপডেটকে ট্রানজিশনে মোড়ান, নিশ্চিত করুন যে এটি startTransition কলের সময় ঘটে:

startTransition(() => {
// ✅ Setting state *during* startTransition call
setPage('/about');
});

startTransition এ আপনি যে ফাংশন পাস করবেন তা অবশ্যই একই সময়ে হতে হবে।

আপনি এভাবে একটি আপডেটকে ট্রানজিশন হিসেবে চিহ্নিত করতে পারবেন না:

startTransition(() => {
// ❌ Setting state *after* startTransition call
setTimeout(() => {
setPage('/about');
}, 1000);
});

বরং, আপনি এটি করতে পারেন:

setTimeout(() => {
startTransition(() => {
// ✅ Setting state *during* startTransition call
setPage('/about');
});
}, 1000);

একইভাবে, আপনি একটি আপডেটকে এইরকম ট্রানজিশন হিসেবে চিহ্নিত করতে পারবেন না:

startTransition(async () => {
await someAsyncFunction();
// ❌ Setting state *after* startTransition call
setPage('/about');
});

তবে, এটি এর পরিবর্তে কাজ করে:

await someAsyncFunction();
startTransition(() => {
// ✅ Setting state *during* startTransition call
setPage('/about');
});

আমি একটি কম্পোনেন্টের বাইরে থেকে useTransition কল করতে চাই

আপনি একটি কম্পোনেন্টের বাইরে থেকে useTransition কল করতে পারবেন না কারণ এটি একটি Hook। এই ক্ষেত্রে, এর পরিবর্তে standalone startTransition পদ্ধতি ব্যবহার করুন। এটি একইভাবে কাজ করে, কিন্তু এটি isPending ইন্ডিকেটর প্রদান করে না।


আমি যে ফাংশন startTransition-এ পাস করি তা তাৎক্ষণিকভাবে কার্যকর হয়

আপনি যদি এই কোডটি চালান, তা 1, 2, 3 প্রিন্ট করবে:

console.log(1);
startTransition(() => {
console.log(2);
setPage('/about');
});
console.log(3);

এটি 1, 2, 3 প্রিন্ট করার কথা। startTransition-এ আপনি যে ফাংশন পাস করেন তা দেরি হয় না। ব্রাউজারের setTimeout-এর মতো, এটি পরে কলব্যাক চালায় না। React আপনার ফাংশনটি তাৎক্ষণিকভাবে কার্যকর করে, কিন্তু এটি চালানোর সময় নির্ধারিত কোনো state আপডেটগুলি ট্রানজিশন হিসাবে চিহ্নিত হয়। এটি এভাবে কাজ করে বলে আপনি কল্পনা করতে পারেন:

// A simplified version of how React works

let isInsideTransition = false;

function startTransition(scope) {
isInsideTransition = true;
scope();
isInsideTransition = false;
}

function setState() {
if (isInsideTransition) {
// ... schedule a Transition state update ...
} else {
// ... schedule an urgent state update ...
}
}