useContext
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"
পাবে।
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'
মান এর জন্য পুনরায় রেন্ডার হবে।
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
সম্পর্কে আরও পড়ুন।
Troubleshooting
My component doesn’t see the value from my provider
There are a few common ways that this can happen:
- You’re rendering
<SomeContext.Provider>
in the same component (or below) as where you’re callinguseContext()
. Move<SomeContext.Provider>
above and outside the component callinguseContext()
. - You may have forgotten to wrap your component with
<SomeContext.Provider>
, or you might have put it in a different part of the tree than you thought. Check whether the hierarchy is right using React DevTools. - You might be running into some build issue with your tooling that causes
SomeContext
as seen from the providing component andSomeContext
as seen by the reading component to be two different objects. This can happen if you use symlinks, for example. You can verify this by assigning them to globals likewindow.SomeContext1
andwindow.SomeContext2
and then checking whetherwindow.SomeContext1 === window.SomeContext2
in the console. If they’re not the same, fix that issue on the build tool level.
I am always getting undefined
from my context although the default value is different
You might have a provider without a value
in the tree:
// 🚩 Doesn't work: no value prop
<ThemeContext.Provider>
<Button />
</ThemeContext.Provider>
If you forget to specify value
, it’s like passing value={undefined}
.
You may have also mistakingly used a different prop name by mistake:
// 🚩 Doesn't work: prop should be called "value"
<ThemeContext.Provider theme={theme}>
<Button />
</ThemeContext.Provider>
In both of these cases you should see a warning from React in the console. To fix them, call the prop value
:
// ✅ Passing the value prop
<ThemeContext.Provider value={theme}>
<Button />
</ThemeContext.Provider>
Note that the default value from your createContext(defaultValue)
call is only used if there is no matching provider above at all. If there is a <SomeContext.Provider value={undefined}>
component somewhere in the parent tree, the component calling useContext(SomeContext)
will receive undefined
as the context value.