useContext je React Hook koji vam omogućava da čitate i da se pretplatite na context iz vaše komponente.

const value = useContext(SomeContext)

Reference

useContext(SomeContext)

Pozovite useContext na vrhu vaše komponente da pročitate i da se pretplatite na context.

import { useContext } from 'react';

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

Pogledajte još primera ispod.

Parametri

  • SomeContext: Context koji ste prethodno kreirali sa createContext. Sam context ne sadrži informaciju, već samo predstavlja vrstu informacije koju možete pružiti i čitati iz komponenti.

Povratne vrednosti

useContext vraća vrednost context-a za pozivajuću komponentu. Određuje se kao value prosleđena najbližem SomeContext-u iznad pozivajuće komponente u stablu. Ako ne postoji takav provider, onda će povratna vrednost biti defaultValue koju ste prosledili u createContext za taj context. Povratna vrednost je uvek ažurna. React automatski ponovo renderuje komponente koje čitaju neki context koji se promenio.

Upozorenja

  • Na useContext() poziv u komponenti ne utiču provider-i vraćeni iz iste komponente. Odgovarajući <Context> mora biti iznad komponente koja poziva useContext().
  • React automatski ponovo renderuje svu decu koja koriste specifičan context počevši od provider-a koji prima drugačiji value. Prethodna i naredna vrednost se porede pomoću Object.is poređenja. Preskakanje ponovnih rendera sa memo ne sprečava da deca prime nove vrednosti context-a.
  • Ako vaš sistem izgradnje pravi duplirane module na izlazu (što se može desiti sa simboličkim linkovima (eng. symlink)), možete slomiti context. Prosleđivanje nečega kroz context radi samo ako su SomeContext koji koristite za pružanje context-a i SomeContext koji koristite za njegovo čitanje potpuno isti objekat, utvrđeno === poređenjem.

Upotreba

Prosleđivanje podataka duboko u stablo

Pozovite useContext na vrhu vaše komponente da pročitate i da se pretplatite na context.

import { useContext } from 'react';

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

useContext vraća vrednost context-a za context koji ste prosledili. Da bi odredio vrednost context-a, React pretražuje stablo komponente i pronalazi najbližeg context provider-a iznad za taj specifični context.

Da biste prosledili context u Button, obmotajte jednu od njegovih roditeljskih komponenata sa odgovarajućim context provider-om:

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

function Form() {
// ... renderuje dugmiće unutra ...
}

Nije bitno koliko slojeva komponenata postoji između provider-a i Button-a. Kada Button negde unutar Form-a pozove useContext(ThemeContext), primiće "dark" kao vrednost.

Pitfall

useContext() uvek traži najbližeg provider-a iznad komponente koja ga poziva. Traži nagore i ne razmatra provider-e u komponenti u kojoj je pozvan 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="Dobro došli">
      <Button>Registruj se</Button>
      <Button>Uloguj se</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>
  );
}


Ažuriranje podataka prosleđenih kroz context

Često ćete želeti da se context vremenom menja. Da biste ažurirali context, kombinujte ga sa state-om. Deklarišite state promenljivu u roditeljskoj komponenti i prosledite trenutni state provider-u kao vrednost context-a.

function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Promeni na svetlu temu
</Button>
</ThemeContext>
);
}

Sada će svaki Button unutar provider-a primiti trenutnu theme vrednost. Ako pozovete setTheme da ažurirate theme vrednost koju prosleđujete provider-u, sve Button komponente će se ponovo renderovati sa novom 'light' vrednošću.

Primeri ažuriranja context-a

Primer 1 od 5:
Ažuriranje vrednosti kroz context

U ovom primeru, MyApp komponenta čuva state promenljivu koja se prosleđuje u ThemeContext provider. Štikliranjem “Tamni režim” checkbox-a, ažurira se state. Promena pružene vrednosti ponovo renderuje sve komponente koje koriste taj 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')
          }}
        />
        Koristi tamni režim
      </label>
    </ThemeContext>
  )
}

function Form({ children }) {
  return (
    <Panel title="Dobro došli">
      <Button>Registruj se</Button>
      <Button>Uloguj se</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>
  );
}

Primetite da value="dark" prosleđuje "dark" string, a value={theme} prosleđuje vrednost JavaScript-ove theme promenljive sa JSX vitičastim zagradama. Vitičaste zagrade vam takođe omogućavaju da context-u prosledite vrednosti koje nisu stringovi.


Specificiranje default vrednosti

Ako React ne može da pronađe nijednog provider-a za taj specifični context u stablu roditelja, vrednost context-a koju vraća useContext() će biti jednaka default vrednosti koju ste specificirali prilikom kreiranja tog context-a:

const ThemeContext = createContext(null);

Default vrednost se nikad ne menja. Ako želite ažurirati context, koristite ga uz state kao što je gore opisano.

Često, umesto null, postoji značajnija vrednost koju možete koristiti kao default, na primer:

const ThemeContext = createContext('light');

Na ovaj način, ako slučajno renderujete neku komponentu bez odgovarajućeg provider-a, sve će raditi. Ovo takođe pomaže vašim komponentama da rade dobro u testnom okruženju bez podešavanja mnogo provider-a u testovima.

U primeru ispod, “Promeni temu” dugme je uvek svetlo jer je izvan svih provider-a theme context-a, a default vrednost za context je 'light'. Probajte da promenite default temu da bude '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');
      }}>
        Promeni temu
      </Button>
    </>
  )
}

function Form({ children }) {
  return (
    <Panel title="Dobro došli">
      <Button>Registruj se</Button>
      <Button>Uloguj se</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>
  );
}


Override-ovanje context-a za deo stabla

Možete override-ovati context za deo stabla obmotavanjem tog dela sa provider-om koji ima drugačiju vrednost.

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

Možete ugnježdavati i override-ovati provider-e koliko god puta želite.

Primeri override-ovanja context-a

Primer 1 od 2:
Override-ovanje teme

Ovde, dugme unutar Footer-a prima drugačiju vrednost context-a ("light") od dugmića izvan ("dark").

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

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

function Form() {
  return (
    <Panel title="Dobro došli">
      <Button>Registruj se</Button>
      <Button>Uloguj se</Button>
      <ThemeContext value="light">
        <Footer />
      </ThemeContext>
    </Panel>
  );
}

function Footer() {
  return (
    <footer>
      <Button>Podešavanja</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>
  );
}


Optimizovanje ponovnih rendera prilikom prosleđivanja objekata i funkcija

Kroz context možete proslediti bilo koju vrednost, uključujući objekte i funkcije.

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

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

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

Ovde je vrednost context-a JavaScript objekat sa dva polja, od kojih je jedno funkcija. Kad god se MyApp ponovo renderuje (na primer, nakon promene rute), ovo će biti drugačiji objekat koji pokazuje na drugačiju funkciju, pa će React takođe trebati da ponovo renderuje sve komponente duboko u stablu koje pozivaju useContext(AuthContext).

Ovo nije problem u manjim aplikacijama. Međutim, nema potrebe da ih ponovo renderujete ako se podaci, poput currentUser-a, nisu promenili. Da biste pomogli React-u da iskoristi tu činjenicu, možete obmotati login funkciju sa useCallback, a kreiranje objekta sa useMemo. Ovo je optimizacija performansi:

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

Kao rezultat ove promene, čak iako MyApp treba ponovo da se renderuje, komponente koje pozivaju useContext(AuthContext) neće morati da se ponovo renderuju dok se currentUser ne promeni.

Pročitajte više o useMemo i useCallback.


Rešavanje problema

Moja komponenta ne vidi vrednost iz provider-a

Postoji par uobičajenih razloga da se ovo desi:

  1. Renderujete <SomeContext> u istoj komponenti (ili ispod) u kojoj pozivate useContext(). Pomerite <SomeContext> iznad i izvan komponente koja poziva useContext().
  2. Možda ste zaboravili da obmotate vašu komponentu sa <SomeContext> ili ste je možda postavili u drugi deo stabla. Proverite da li je hijerarhija ispravna upotrebom React DevTools-a.
  3. Možda nailazite na problem sa alatima tokom izgradnje koji prouzrokuje da SomeContext, kako ga vidi komponenta koja ga pruža, i SomeContext, kako ga vidi komponenta koja ga čita, budu dva različita objekta. Ovo se može desiti ako koristite simboličke linkove, na primer. Možete verifikovati ovo tako što ćete im dodeliti globalne promenljive poput window.SomeContext1 i window.SomeContext2, a onda u konzoli proveriti window.SomeContext1 === window.SomeContext2. Ako nisu jednaki, popravite taj problem na nivou alata za izgradnju.

Uvek dobijam undefined iz mog context-a iako je default vrednost drugačija

Možda imate provider bez value u stablu:

// 🚩 Ne radi: nema value prop
<ThemeContext>
<Button />
</ThemeContext>

Ako zaboravite specificirati value, to je kao da prosleđujete value={undefined}.

Možda ste greškom iskoristili drugo ime za prop:

// 🚩 Ne radi: prop treba da se zove "value"
<ThemeContext theme={theme}>
<Button />
</ThemeContext>

U oba slučaja trebate videti upozorenje od React-a u konzoli. Da biste ovo popravili, nazovite prop value:

// ✅ Prosleđivanje value prop-a
<ThemeContext value={theme}>
<Button />
</ThemeContext>

Primetite da se default vrednost iz vašeg createContext(defaultValue) poziva koristi samo ako uopšte nema odgovarajućeg provider-a iznad. Ako postoji komponenta sa <SomeContext value={undefined}> negde u stablu roditelja, komponenta koja poziva useContext(SomeContext) će dobiti undefined kao vrednost context-a.