useContext एक React हुक है जो आपको अपने कौम्पोनॅन्ट से कॉन्टेक्स्ट को पढ़ने और सब्सक्राइब करने की सुविधा देता है।

const value = useContext(SomeContext)

रेफरेंस

useContext(SomeContext)

अपने कौम्पोनॅन्ट के टॉप लेवल पर useContext कॉल करें ताकि आप कॉन्टेक्स्ट को पढ़ और सब्सक्राइब कर सकें।

import { useContext } from 'react';

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

नीचे और उदाहरण देखें।

पैरामीटर्स

  • SomeContext: वह कॉन्टेक्स्ट जिसे आपने पहले createContext के साथ बनाया है। खुद कॉन्टेक्स्ट में जानकारी नहीं होती, यह केवल उस प्रकार की जानकारी को दर्शाता है जिसे आप कौम्पोनॅन्ट्स से प्रोवाइड या पढ़ सकते हैं।

रिटर्न्स

useContext कॉल करने वाले कौम्पोनॅन्ट के लिए कॉन्टेक्स्ट वैल्यू रिटर्न करता है। यह वैल्यू उस सबसे नज़दीकी SomeContext प्रोवाइडर के value के अनुसार तय होती है, जो कौम्पोनॅन्ट ट्री में ऊपर होता है। अगर ऐसा कोई प्रोवाइडर नहीं है, तो रिटर्न की गई वैल्यू createContext में दिए गए defaultValue के बराबर होगी। रिटर्न की गई वैल्यू हमेशा अपडेटेड रहती है। React अपने-आप उन कौम्पोनॅन्ट्स को री-रेंडर करता है जो किसी कॉन्टेक्स्ट को पढ़ते हैं, अगर वह बदलता है।

सावधानियाँ

  • किसी कौम्पोनॅन्ट में useContext() कॉल उसी कौम्पोनॅन्ट से रिटर्न हुए प्रोवाइडर्स से प्रभावित नहीं होती। संबंधित <Context> ज़रूरी है कि वह उस कौम्पोनॅन्ट के ऊपर हो जो useContext() कॉल कर रहा है।
  • React अपने-आप उन सभी चिल्ड्रन को री-रेंडर करता है जो किसी विशेष कॉन्टेक्स्ट का उपयोग करते हैं, उस प्रोवाइडर से शुरू होकर जिसे नया value मिलता है। पिछली और अगली वैल्यू की तुलना Object.is से होती है। memo से री-रेंडर स्किप करने पर भी चिल्ड्रन को नई कॉन्टेक्स्ट वैल्यू मिलती है।
  • अगर आपका बिल्ड सिस्टम आउटपुट में डुप्लिकेट मॉड्यूल्स बनाता है (जैसा कि symlinks के साथ हो सकता है), तो इससे कॉन्टेक्स्ट टूट सकता है। कॉन्टेक्स्ट के ज़रिए कुछ पास करना तभी काम करता है जब प्रोवाइड करने और पढ़ने के लिए उपयोग किया गया SomeContext बिल्कुल वही ऑब्जेक्ट हो, जैसा कि === तुलना से तय होता है।

उपयोग

डेटा को ट्री में गहराई तक पास करना

अपने कौम्पोनॅन्ट के टॉप लेवल पर useContext कॉल करें ताकि आप कॉन्टेक्स्ट को पढ़ और सब्सक्राइब कर सकें।

import { useContext } from 'react';

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

useContext आपके द्वारा पास किए गए कॉन्टेक्स्ट के लिए कॉन्टेक्स्ट वैल्यू रिटर्न करता है। इस वैल्यू को निर्धारित करने के लिए, React कौम्पोनॅन्ट ट्री में ऊपर की ओर खोजता है और उस विशेष कॉन्टेक्स्ट के लिए सबसे नज़दीकी कॉन्टेक्स्ट प्रोवाइडर ढूंढता है।

अगर आप किसी Button को कॉन्टेक्स्ट देना चाहते हैं, तो उसे या उसके किसी पैरेंट कौम्पोनॅन्ट को संबंधित कॉन्टेक्स्ट प्रोवाइडर में रैप करें:

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

function Form() {
// ... बटन को अंदर रेंडर करता है ...
}

इससे कोई फर्क नहीं पड़ता कि प्रोवाइडर और Button के बीच कितनी लेयर्स हैं। जब भी Form के अंदर कहीं भी कोई Button useContext(ThemeContext) कॉल करता है, उसे "dark" वैल्यू मिलेगी।

Pitfall

useContext() हमेशा उस कौम्पोनॅन्ट के ऊपर सबसे नज़दीकी प्रोवाइडर को खोजता है जो इसे कॉल करता है। यह ऊपर की ओर खोजता है और उस कौम्पोनॅन्ट के अंदर के प्रोवाइडर्स को नहीं देखता जिसमें आप useContext() कॉल कर रहे हैं।

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>
  );
}


कॉन्टेक्स्ट के ज़रिए पास किए गए डेटा को अपडेट करना

अक्सर, आप चाहेंगे कि कॉन्टेक्स्ट समय के साथ बदल सके। कॉन्टेक्स्ट को अपडेट करने के लिए, इसे स्टेट के साथ मिलाएँ। पैरेंट कौम्पोनॅन्ट में एक स्टेट वेरिएबल डिक्लेयर करें, और वर्तमान स्टेट को कॉन्टेक्स्ट वैल्यू के रूप में प्रोवाइडर को पास करें।

function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
लाइट थीम पर स्विच करें
</Button>
</ThemeContext>
);
}

अब प्रोवाइडर के अंदर का कोई भी Button वर्तमान theme वैल्यू प्राप्त करेगा। यदि आप प्रोवाइडर को पास की गई theme वैल्यू को अपडेट करने के लिए setTheme कॉल करते हैं, तो सभी Button कौम्पोनॅन्ट्स नई 'light' वैल्यू के साथ री-रेंडर हो जाएँगे।

कॉन्टेक्स्ट को अपडेट करने के उदाहरण

Example 1 of 5:
कॉन्टेक्स्ट के ज़रिए वैल्यू अपडेट करना

इस उदाहरण में, MyApp कौम्पोनॅन्ट एक स्टेट वेरिएबल होल्ड करता है जिसे बाद में ThemeContext प्रोवाइडर को पास किया जाता है। “डार्क मोड” चेकबॉक्स को चेक करने से स्टेट अपडेट होती है। प्रोवाइड की गई वैल्यू बदलने पर उस कॉन्टेक्स्ट का उपयोग करने वाले सभी कौम्पोनॅन्ट्स री-रेंडर हो जाते हैं।

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" स्ट्रिंग पास करता है, लेकिन value={theme} जावास्क्रिप्ट के theme वेरिएबल की वैल्यू पास करता है, जिसमें JSX कर्ली ब्रेसेस का उपयोग होता है। कर्ली ब्रेसेस की मदद से आप ऐसी कॉन्टेक्स्ट वैल्यू भी पास कर सकते हैं जो स्ट्रिंग न हों।


एक फॉलबैक डिफॉल्ट वैल्यू निर्दिष्ट करना

अगर React पैरेंट ट्री में उस विशेष कॉन्टेक्स्ट का कोई प्रोवाइडर नहीं ढूंढ पाता, तो useContext() द्वारा रिटर्न की गई कॉन्टेक्स्ट वैल्यू उस डिफॉल्ट वैल्यू के बराबर होगी जिसे आपने वह कॉन्टेक्स्ट बनाते समय निर्दिष्ट किया था:

const ThemeContext = createContext(null);

डिफॉल्ट वैल्यू कभी नहीं बदलती। अगर आप कॉन्टेक्स्ट को अपडेट करना चाहते हैं, तो ऊपर बताए अनुसार इसे स्टेट के साथ उपयोग करें।

अक्सर, null की जगह आप कोई और अधिक अर्थपूर्ण वैल्यू डिफॉल्ट के रूप में दे सकते हैं, उदाहरण के लिए:

const ThemeContext = createContext('light');

इस तरह, अगर आप गलती से किसी कौम्पोनॅन्ट को बिना संबंधित प्रोवाइडर के रेंडर कर देते हैं, तो भी वह टूटेगा नहीं। यह आपके कौम्पोनॅन्ट्स को टेस्ट एनवायरनमेंट में भी बिना ज़्यादा प्रोवाइडर्स सेट किए अच्छे से काम करने में मदद करता है।

नीचे दिए गए उदाहरण में, “Toggle theme” बटन हमेशा light रहता है क्योंकि वह किसी भी थीम कॉन्टेक्स्ट प्रोवाइडर के बाहर है और डिफॉल्ट कॉन्टेक्स्ट थीम वैल्यू '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>
  );
}


ट्री के किसी हिस्से के लिए कॉन्टेक्स्ट ओवरराइड करना

आप ट्री के किसी हिस्से के लिए कॉन्टेक्स्ट को ओवरराइड कर सकते हैं, बस उस हिस्से को अलग वैल्यू वाले प्रोवाइडर में रैप करें।

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

आप जितनी बार चाहें, प्रोवाइडर्स को नेस्ट और ओवरराइड कर सकते हैं।

कॉन्टेक्स्ट ओवरराइड करने के उदाहरण

Example 1 of 2:
थीम ओवरराइड करना

यहाँ, Footer के अंदर वाला बटन बाहर के बटनों ("dark") की तुलना में अलग कॉन्टेक्स्ट वैल्यू ("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>
  );
}


ऑब्जेक्ट्स और फंक्शन्स पास करते समय री-रेंडर को ऑप्टिमाइज़ करना

आप कॉन्टेक्स्ट के ज़रिए कोई भी वैल्यू पास कर सकते हैं, जिनमें ऑब्जेक्ट्स और फंक्शन्स भी शामिल हैं।

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

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

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

यहाँ, कॉन्टेक्स्ट वैल्यू एक जावास्क्रिप्ट ऑब्जेक्ट है जिसमें दो प्रॉपर्टीज़ हैं, जिनमें से एक एक फंक्शन है। जब भी MyApp री-रेंडर होता है (जैसे कि रूट अपडेट पर), यह एक नया ऑब्जेक्ट और नया फंक्शन बनेगा, इसलिए React को ट्री में गहराई तक उन सभी कौम्पोनॅन्ट्स को भी री-रेंडर करना पड़ेगा जो useContext(AuthContext) कॉल करते हैं।

छोटी ऍप्स में यह कोई समस्या नहीं है। हालाँकि, अगर अंदर का डेटा, जैसे कि currentUser, नहीं बदला है तो उन्हें री-रेंडर करने की ज़रूरत नहीं है। React को इसका लाभ उठाने में मदद करने के लिए, आप login फंक्शन को useCallback से रैप कर सकते हैं और ऑब्जेक्ट क्रिएशन को useMemo में रख सकते हैं। यह एक परफॉर्मेंस ऑप्टिमाइज़ेशन है:

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) कॉल करते हैं, उन्हें तब तक री-रेंडर नहीं करना पड़ेगा जब तक currentUser नहीं बदला है।

useMemo और useCallback के बारे में और पढ़ें।


समस्या निवारण

मेरा कौम्पोनॅन्ट मेरे प्रोवाइडर से वैल्यू नहीं देख पा रहा

ऐसा होने के कुछ सामान्य कारण हैं:

  1. आप <SomeContext> को उसी कौम्पोनॅन्ट (या उसके नीचे) में रेंडर कर रहे हैं जहाँ आप useContext() कॉल कर रहे हैं। <SomeContext> को उस कौम्पोनॅन्ट के ऊपर और बाहर ले जाएँ जो useContext() कॉल करता है।
  2. हो सकता है आपने अपने कौम्पोनॅन्ट को <SomeContext> के साथ रैप करना भूल गए हों, या आपने उसे ट्री के उस हिस्से में रखा हो जहाँ आप सोच रहे थे वहाँ नहीं है। React DevTools का उपयोग करके देखें कि हायरार्की सही है या नहीं।
  3. हो सकता है आपके टूलिंग में कोई बिल्ड इश्यू हो, जिससे प्रोवाइड करने वाले कौम्पोनॅन्ट से दिखने वाला SomeContext और पढ़ने वाले कौम्पोनॅन्ट से दिखने वाला SomeContext दो अलग-अलग ऑब्जेक्ट्स बन गए हों। ऐसा symlinks के उपयोग से हो सकता है। आप इन्हें ग्लोबल्स जैसे window.SomeContext1 और window.SomeContext2 में असाइन करके और फिर कंसोल में window.SomeContext1 === window.SomeContext2 चेक करके वेरिफाई कर सकते हैं। अगर ये समान नहीं हैं, तो बिल्ड टूल लेवल पर उस समस्या को ठीक करें।

मुझे हमेशा अपने कॉन्टेक्स्ट से undefined मिल रहा है, जबकि डिफॉल्ट वैल्यू अलग है

हो सकता है आपके ट्री में कोई प्रोवाइडर बिना value के हो:

// 🚩 काम नहीं करेगा: कोई value प्रॉप नहीं है
<ThemeContext>
<Button />
</ThemeContext>

अगर आप value को निर्दिष्ट करना भूल जाते हैं, तो यह ऐसा है जैसे आप value={undefined} पास कर रहे हैं।

आपने गलती से कोई दूसरा प्रॉप नाम भी इस्तेमाल कर लिया हो सकता है:

// 🚩 काम नहीं करेगा: प्रॉप का नाम "value" होना चाहिए
<ThemeContext theme={theme}>
<Button />
</ThemeContext>

इन दोनों ही मामलों में आपको React की तरफ से कंसोल में एक चेतावनी दिखनी चाहिए। इन्हें ठीक करने के लिए, प्रॉप को value कहें:

// ✅ value प्रॉप पास करना
<ThemeContext value={theme}>
<Button />
</ThemeContext>

ध्यान दें कि createContext(defaultValue) कॉल से मिलने वाली डिफॉल्ट वैल्यू का उपयोग तभी होता है जब ऊपर कहीं भी कोई मिलती-जुलती प्रोवाइडर न हो। अगर पैरेंट ट्री में कहीं <SomeContext value={undefined}> कौम्पोनॅन्ट है, तो useContext(SomeContext) कॉल करने वाले कौम्पोनॅन्ट को कॉन्टेक्स्ट वैल्यू के रूप में undefined ही मिलेगा।