useContext হচ্ছে একটি React হুক যেটা আপনাকে কম্পোনেন্ট থেকে context পড়তে এবং সাবস্কাইব করতে দিবে।

const value = useContext(SomeContext)

রেফারেন্স

useContext(SomeContext)

context রিড করা এবং সেখানে সাবস্ক্রাইব করার জন্য useContext কে আপনার কম্পোনেন্ট এর একেবারে উপরের স্তরে কল করতে হবে।

import { useContext } from 'react';

function MyComponent() {
const theme = useContext(ThemeContext);
// ...

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

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

  • SomeContext: এটি হচ্ছে সেই context যেটি আপনি আগে createContext ব্যবহার করে তৈরি করেছিলেন। context নিজে থেকে তথ্য ধারণ করে না, এটি শুধুমাত্র সেই তথ্যগুলিকেই উপস্থাপন করে যা আপনি দিতে পারেন অথবা কম্পোনেন্ট থেকে রিড করতে পারেন।

রিটার্নস

যে কম্পোনেন্টে useContext কল করা হয় তার জন্য context এর ভ্যালু রিটার্ন করে। যে কম্পোনেন্ট কল করা হয়েছে তার উপরে কম্পোনেন্ট ট্রির সব থেকে কাছের SomeContext.Provider দ্বারা নির্ধারিত হয় যা value হিসাবে পাঠানো হয়। যদি এমন কোন provider না থাকে, তাহলে তার রিটার্ন করা মান হবে defaultValue যা আপনি context এর জন্য createContext এ পাঠিয়েছিলেন। রিটার্ন্ড মান সবসময় আপ টু ডেট হবে। যদি context এ কোন পরিবর্তন হয়, তাহলে context ব্যবহারকৃত কম্পোনেন্টগুলোকে React স্বয়ংক্রিয়ভাবে পুনরায় রেন্ডার করবে।

সতর্কতা

  • একটি কম্পোনেন্টের ভিতর useContext() কল করা হলে সেই একই কম্পোনেন্ট থেকে রিটার্নড হওয়া providers দিয়ে এটি প্রভাবিত হবে না। যে কম্পোনেন্ট থেকে useContext() কল করা হয়েছে, সংশ্লিষ্ট <Context.Provider> কে সেই কম্পোনেন্টের উপরে থাকতে হবে
  • provider এর শুরু থেকে যেসব চিলড্রেন একটি নির্দিষ্ট context ব্যবহার করে সেটি যখন ভিন্ন মান গ্রহণ করে তখন React সেসব চিলড্রেনকে স্বয়ংক্রিয়ভাবে পুনরায় রেন্ডার করে। আগের এবং পরের মান Object.is এর মাধ্যমে তুলনা করা হয়। পুনরায় রেন্ডার এড়িয়ে যেতে যেসব চিলড্রেনে memo ব্যবহার করা হয়, সেখানে context এর নতুন মান পেতে বাধা দেওয়া হয় না।
  • যদি আপনার বিল্ড সিস্টেম আউটপুটে ডুপ্লিকেট মডিউল উৎপাদন করে (যা symlinks দ্বারা তৈরি হয়), তাহলে সেটা আপনার context কে ব্রেক করতে পারে। কোন কিছু যখন context এর মাধ্যমে পাঠানো হয়, সেটি শুধুমাত্র তখনই কাজ করবে যখন আপনার context প্রদান করার জন্য ব্যবহারকৃত SomeContext এবং রিড করার জন্য ব্যবহারকৃত SomeContext হুবহু একই object হবে, যা === এর মাধ্যমে তুলনা করে নির্ধারিত হয়।

ব্যবহারবিধি

ট্রির গভীরে ডাটা পাস করা

context রিড করা এবং সেখানে সাবস্ক্রাইব করার জন্য useContext কে আপনার কম্পোনেন্ট এর একেবারে উপরের স্তরে কল করতে হবে।

import { useContext } from 'react';

function Button() {
const theme = useContext(ThemeContext);
// ...

useContext আপনার পাস করা context এর জন্য context এর মান রিটার্ন করে। context এর মান নির্ধারণ করার জন্য, React কম্পোনেন্ট ট্রিতে সার্চ করে এবং এই নির্দিষ্ট context এর জন্য উপরের দিকে সব থেকে কাছের context provider কে খুঁজে বের করে।

একটি Button এ context পাস করতে, এটিকে বা এর প্যারেন্ট কম্পোনেন্টগুলির একটিকে সংশ্লিষ্ট context provider দিয়ে wrap করতে হবেঃ

function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}

function Form() {
// ... renders buttons inside ...
}

Provider এবং Button এর মধ্যে কম্পোনেন্টগুলির কতগুলি স্তর রয়েছে তা বিবেচ্য নয়। যখন Form এর ভিতরে যেকোনো জায়গায় একটি Button useContext(ThemeContext) কল করে, তখন এটি মান হিসাবে "dark" পাবে।

সতর্কতা

useContext() সবসময় একে কল করে এমন কম্পোনেন্টের উপরের দিকে নিকটতম provider কে খুঁজে। এটি উপরের দিকে সার্চ করে এবং আপনি যে কম্পোনেন্ট থেকে useContext() কল করছেন সেখানকার provider গুলোকে বিবেচনা করা হয় না

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}


context পাস করার মাধ্যমে ডাটা আপডেট করা

মাঝে মাঝেই আপনি সময়ের সাথে সাথে context পরিবর্তন করতে চাইবেন। context আপডেট করতে, এটিকে state এর সাথে একত্রে ব্যবহার করতে হবে। প্যারেন্ট কম্পোনেন্টে একটি state ভ্যারিয়েবল ডিক্লেয়ার করতে হবে এবং provider এ context এর মান হিসেবে বর্তমান state কে পাস করে দিতে হবে।

function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Switch to light theme
</Button>
</ThemeContext.Provider>
);
}

এখন provider এর ভিতরে যেকোনো Button বর্তমান theme এর মান পাবে। আপনি provider এর কাছে যে theme এর মানটি পাস করেছেন সেটি আপডেট করতে আপনি setTheme কে কল করলে, সব Button কম্পোনেন্ট নতুন 'light' মান এর জন্য পুনরায় রেন্ডার হবে।

context আপডেট করার উদাহরণ

Example 1 of 5:
context এর মাধ্যমে মান আপডেট করা

এই উদাহরণে, MyApp কম্পোনেন্ট একটি state ভ্যারিয়েবল ধারণ করে যা পরবর্তীতে ThemeContext provider এ পাস করা হয়। “Dark mode” চেকবক্স চেক করলে state আপডেট হয়। প্রদত্ত মানের পরিবর্তন সেই সব কম্পোনেন্টকে পুনরায় রেন্ডার করে যারা এই context ব্যবহার করেছে।

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={theme}>
      <Form />
      <label>
        <input
          type="checkbox"
          checked={theme === 'dark'}
          onChange={(e) => {
            setTheme(e.target.checked ? 'dark' : 'light')
          }}
        />
        Use dark mode
      </label>
    </ThemeContext.Provider>
  )
}

function Form({ children }) {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}

উল্লেখ্য যে value="dark", "dark" কে string হিসাবে পাস করা হচ্ছে, কিন্তু value={theme} জাভাস্ক্রিপ্টের theme ভ্যারিয়েবলের মান JSX curly braces দিয়ে পাস করা হচ্ছে। স্ট্রিং নয় এমন context এর মানগুলিও curly braces পাস করতে দেয়।


ফলব্যাক এর ক্ষেত্রে ডিফল্ট মান নির্ধারণ করা

React যদি প্যারেন্ট ট্রিতে নির্দিষ্ট context এর কোন provider খুঁজে না পায়, তাহলে useContext() থেকে রিটার্ন্ড context এর মান ডিফল্ট মানের সমান হবে যা আপনি সেই context টি তৈরি করার সময় নির্ধারণ করেছিলেনঃ

const ThemeContext = createContext(null);

ডিফল্ট মান কখনই পরিবর্তন হয় না। আপনি যদি context আপডেট করতে চান, তাহলে উপরে বর্ণিত নিয়মে state এর সাথে এটি ব্যবহার করুন।

প্রায়শই, null এর পরিবর্তে আপনি ডিফল্ট মান হিসাবে ব্যবহার করতে পারেন এমন অনেক অর্থপূর্ণ মান রয়েছে, উদাহরণ স্বরূপঃ

const ThemeContext = createContext('light');

এইভাবে, আপনি যদি দুর্ঘটনাক্রমে সংশ্লিষ্ট provider ছাড়া কোন কম্পোনেন্ট রেন্ডার করেন, তবে এটি ভাংবে করবে না। এটি আপনার কম্পোনেন্টগুলিকে টেস্টের সময় অনেক অনেক provider সেট আপ না করে একটি টেস্ট ইনভায়রনমেন্টে ভালভাবে কাজ করতে সহায়তা করে।

নীচের উদাহরণে, “Toggle theme” বাটনটি সবসময় light কারণ এটি সবরকম theme context provider এর বাইরে এবং ডিফল্ট context theme এর মান 'light'। ডিফল্ট থিমকে 'dark' করার জন্য এডিট করন।

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext('light');

export default function MyApp() {
  const [theme, setTheme] = useState('light');
  return (
    <>
      <ThemeContext.Provider value={theme}>
        <Form />
      </ThemeContext.Provider>
      <Button onClick={() => {
        setTheme(theme === 'dark' ? 'light' : 'dark');
      }}>
        Toggle theme
      </Button>
    </>
  )
}

function Form({ children }) {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ children, onClick }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className} onClick={onClick}>
      {children}
    </button>
  );
}


ট্রির একটি অংশের জন্য context ওভাররাইড করা

আপনি ট্রির একটি অংশকে একটি ভিন্ন মান সহ একটি provider দিয়ে wrap করার মাধ্যমে সেই অংশের জন্য context টি ওভাররাইড করতে পারেন।

<ThemeContext.Provider value="dark">
...
<ThemeContext.Provider value="light">
<Footer />
</ThemeContext.Provider>
...
</ThemeContext.Provider>

আপনি যতবার প্রয়োজন ততবার provider গুলিকে নেস্ট এবং ওভাররাইড করতে পারেন।

কিছু উদাহরণ চেষ্টা করে দেখুন

Example 1 of 2:
একটি থিম ওভাররাইড করা

এখানে, Footer এর ভিতরের বাটনটি বাইরের বাটনের ("dark") না পেয়ে একটি ভিন্ন context মান ("light") পায়।

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
      <ThemeContext.Provider value="light">
        <Footer />
      </ThemeContext.Provider>
    </Panel>
  );
}

function Footer() {
  return (
    <footer>
      <Button>Settings</Button>
    </footer>
  );
}

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      {title && <h1>{title}</h1>}
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}


object এবং function পাস করার সময় পুনরায় রেন্ডার অপ্টিমাইজ করা

আপনি object এবং function সহ যেকোনো মান context এর মাধ্যমে পাস করতে পারেন।

function MyApp() {
const [currentUser, setCurrentUser] = useState(null);

function login(response) {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}

return (
<AuthContext.Provider value={{ currentUser, login }}>
<Page />
</AuthContext.Provider>
);
}

এখানে, context এর মান হল একটি JavaScript অবজেক্ট যার দুইটি প্যারামিটার রয়েছে, যার ভিতর একটি function। যখনই MyApp পুনরায় রেন্ডার করে (উদাহরণস্বরূপ, একটি রাউট আপডেটে), তখন এটি একটি ভিন্ন object হবে যা একটি ভিন্ন function নির্দেশ করে, তাই React কেও ট্রির গভীরে সব কম্পোনেন্টকে পুনরায় রেন্ডার করতে হবে যারা useContext(AuthContext) কল করেছে।

ছোট অ্যাপগুলিতে এটি কোনো সমস্যা নয়। যাই হোক, যদি currentUser এর মত অন্তর্নিহিত ডেটা পরিবর্তিত না হয় তবে তাদের পুনরায় রেন্ডার করার কোন প্রয়োজন নেই। এই ক্ষেত্রে React কে সুবিধা নিতে সাহায্য করার জন্য আপনি useCallback দিয়ে login ফাংশনটি wrap করে দিতে পারেন এবং object তৈরিকে useMemo দিয়ে wrap করে দিতে পারেন। এটি একটি পারফরমেন্স অপ্টিমাইজেশানঃ

import { useCallback, useMemo } from 'react';

function MyApp() {
const [currentUser, setCurrentUser] = useState(null);

const login = useCallback((response) => {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}, []);

const contextValue = useMemo(() => ({
currentUser,
login
}), [currentUser, login]);

return (
<AuthContext.Provider value={contextValue}>
<Page />
</AuthContext.Provider>
);
}

এই পরিবর্তনের ফলস্বরূপ, এমনকি যদি MyApp কে পুনরায় রেন্ডার করার প্রয়োজন হয়, তবুও useContext(AuthContext) কল করা কম্পোনেন্ট পুনরায় রেন্ডার করার প্রয়োজন হবে না যদি না বর্তমান ব্যবহারকারী পরিবর্তিত হয়।

useMemo এবং useCallback সম্পর্কে আরও পড়ুন।


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

আমার কম্পোনেন্ট আমার provider এর মান দেখতে পায় না

কিছু সাধারণ কারণে এমনটি ঘটতে পারে:

  1. আপনি যেখানে useContext() কল করছেন সেই কম্পোনেন্টে (অথবা নিচে) <SomeContext.Provider> রেন্ডার করেছেন। যে কম্পোনেন্টে useContext() কল হচ্ছে তার উপরে এবং বাইরে <SomeContext.Provider> কে সরিয়ে ফেলুন।
  2. আপনি হয়ত <SomeContext.Provider> দিয়ে আপনার কম্পোনেন্টকে wrap করতে ভুলে গেছেন, অথবা আপনি এটিকে ট্রির যেখানে রাখার কথা ভেবেছিলেন তার থেকে হয়ত ভিন্ন কোথাও রেখেছেন। React DevTools ব্যবহার করে hierarchy টি সঠিক কি না তা পরীক্ষা করুন।
  3. আপনি হয়ত আপনার টুলিং সম্পর্কিত কিছু বিল্ড সমস্যার ভিতর দিয়ে যাচ্ছেন যার ফলে প্রোভাইডিং কম্পোনেন্ট থেকে SomeContext এবং রিডিং কম্পোনেন্ট থেকে SomeContext ভিন্ন object হিসাবে দেখাচ্ছে। উদাহরণস্বরূপ, আপনি যদি symlink ব্যবহার করেন, আপনি এগুলোকে গ্লোবালে যুক্ত করে (যেমন window.SomeContext1 এবং window.SomeContext2) এবং তারপর কনসোলে window.SomeContext1 === window.SomeContext2 কি না তা পরীক্ষা করে এটি যাচাই করতে পারেন। যদি তারা একই না হয়, তাহলে বিল্ড টুল স্তরে সেই সমস্যাটি ঠিক করুন।

আমি সব সময় আমার context থেকে undefined পাচ্ছি যদিও ডিফল্ট মান ভিন্ন

ট্রিতে হয়ত আপনার একটি value বিহীন provider আছেঃ

// 🚩 Doesn't work: no value prop
<ThemeContext.Provider>
<Button />
</ThemeContext.Provider>

আপনি যদি value উল্লেখ করতে ভুলে যান, তাহলে এটি value={undefined} এরকম কিছু পাস করে।

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

// 🚩 Doesn't work: prop should be called "value"
<ThemeContext.Provider theme={theme}>
<Button />
</ThemeContext.Provider>

উভয় ক্ষেত্রেই আপনি কনসোলে React থেকে একটি ওয়ার্নিং দেখতে পাবেন। তাদের ঠিক করতে, প্রপ হিসাবে value কল করুন:

// ✅ Passing the value prop
<ThemeContext.Provider value={theme}>
<Button />
</ThemeContext.Provider>

মনে রাখবেন যে আপনার createContext(defaultValue) কল থেকে ডিফল্ট মানটি শুধুমাত্র তখনই ব্যবহার করা হয় যদি উপরে কোন provider এর সাথে মিল না পায়। যদি প্যারেন্ট ট্রিতে কোথাও একটি <SomeContext.Provider value={undefined}> কম্পোনেন্ট থাকে, তাহলে যে কম্পোনেন্ট useContext(SomeContext) কল করছে সে context এর মান হিসাবে undefined পাবে।