useContext
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);
// ...
Parametri
SomeContext
: Context koji ste prethodno kreirali sacreateContext
. 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 pozivauseContext()
. - 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ćuObject.is
poređenja. Preskakanje ponovnih rendera samemo
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 iSomeContext
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.
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.
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.
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:
- Renderujete
<SomeContext>
u istoj komponenti (ili ispod) u kojoj pozivateuseContext()
. Pomerite<SomeContext>
iznad i izvan komponente koja pozivauseContext()
. - 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. - Možda nailazite na problem sa alatima tokom izgradnje koji prouzrokuje da
SomeContext
, kako ga vidi komponenta koja ga pruža, iSomeContext
, 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 poputwindow.SomeContext1
iwindow.SomeContext2
, a onda u konzoli proveritiwindow.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.