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 এর কাছে পাঠানো value দ্বারা নির্ধারিত হয়। যদি এমন কোন provider না থাকে, তাহলে রিটার্ন করা মান হবে defaultValue যা আপনি সেই context এর জন্য createContext এ পাঠিয়েছিলেন। রিটার্ন করা মান সবসময় আপ-টু-ডেট থাকে। যদি context পরিবর্তিত হয়, তাহলে React স্বয়ংক্রিয়ভাবে সেই কম্পোনেন্টগুলোকে পুনরায় রেন্ডার করে যারা কোন context পড়ে।

সতর্কতা

  • একটি কম্পোনেন্টের ভিতর useContext() কল করা হলে সেই একই কম্পোনেন্ট থেকে রিটার্ন করা provider দিয়ে এটি প্রভাবিত হয় না। যে কম্পোনেন্ট থেকে useContext() কল করা হয়েছে, সংশ্লিষ্ট <Context> কে সেই কম্পোনেন্টের উপরে থাকতে হবে
  • React স্বয়ংক্রিয়ভাবে পুনরায় রেন্ডার করে সেইসব চিলড্রেনকে যারা একটি নির্দিষ্ট context ব্যবহার করে, শুরু করে সেই provider থেকে যা ভিন্ন value পায়। আগের এবং পরের মান 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 value="dark">
<Form />
</ThemeContext>
);
}

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 value="dark">
      <Form />
    </ThemeContext>
  )
}

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 value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Switch to light theme
</Button>
</ThemeContext>
);
}

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

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

উদাহরণ 1 / 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 value={theme}>
      <Form />
      <label>
        <input
          type="checkbox"
          checked={theme === 'dark'}
          onChange={(e) => {
            setTheme(e.target.checked ? 'dark' : 'light')
          }}
        />
        Use dark mode
      </label>
    </ThemeContext>
  )
}

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 value={theme}>
        <Form />
      </ThemeContext>
      <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 value="dark">
...
<ThemeContext value="light">
<Footer />
</ThemeContext>
...
</ThemeContext>

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

Examples of overriding context

উদাহরণ 1 / 2:
একটি থিম ওভাররাইড করা

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

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

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

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
      <ThemeContext value="light">
        <Footer />
      </ThemeContext>
    </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 value={{ currentUser, login }}>
<Page />
</AuthContext>
);
}

এখানে, 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 value={contextValue}>
<Page />
</AuthContext>
);
}

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

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


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

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

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

  1. আপনি যেখানে useContext() কল করছেন সেই কম্পোনেন্টে (অথবা নিচে) <SomeContext> রেন্ডার করেছেন। যে কম্পোনেন্টে useContext() কল হচ্ছে তার উপরে এবং বাইরে <SomeContext> কে সরিয়ে ফেলুন।
  2. আপনি হয়ত <SomeContext> দিয়ে আপনার কম্পোনেন্টকে 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>
<Button />
</ThemeContext>

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

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

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

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

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