useContext

Nota bene

Questa pagina è stata tradotta automaticamente e supervisionata da un maintainer. Un’ulteriore revisione da parte della community sarebbe comunque utile. Migliora questa traduzione.

useContext è un Hook React che ti permette di leggere e sottoscriverti al context dal tuo componente.

const value = useContext(SomeContext)

Reference

useContext(SomeContext)

Chiama useContext al top level del tuo componente per leggere e sottoscriverti al context.

import { useContext } from 'react';

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

Vedi altri esempi sotto.

Parameters

  • SomeContext: Il context che hai creato in precedenza con createContext. Il context in sé non contiene informazioni: rappresenta solo il tipo di informazione che puoi fornire o leggere dai componenti.

Returns

useContext restituisce il valore del context per il componente chiamante. È determinato dal value passato al SomeContext più vicino sopra il componente chiamante nell’albero. Se non c’è un provider, il valore restituito sarà il defaultValue che hai passato a createContext per quel context. Il valore restituito è sempre aggiornato. React ri-renderizza automaticamente i componenti che leggono un context se questo cambia.

Caveats

  • Una chiamata a useContext() in un componente non è influenzata dai provider restituiti dallo stesso componente. Il corrispondente <Context> deve trovarsi sopra il componente che esegue la chiamata a useContext().
  • React ri-renderizza automaticamente tutti i figli che usano un particolare context a partire dal provider che riceve un value diverso. I valori precedente e successivo vengono confrontati con il confronto Object.is. Saltare le ri-renderizzazioni con memo non impedisce ai figli di ricevere valori di context aggiornati.
  • Se il tuo sistema di build produce moduli duplicati nell’output (cosa che può accadere con i symlink), questo può rompere il context. Passare qualcosa tramite context funziona solo se il SomeContext che usi per fornire il context e il SomeContext che usi per leggerlo sono esattamente lo stesso oggetto, come determinato da un confronto ===.

Usage

Passare dati in profondità nell’albero

Chiama useContext al top level del tuo componente per leggere e sottoscriverti al context.

import { useContext } from 'react';

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

useContext restituisce il valore del context per il context che hai passato. Per determinare il valore del context, React cerca nell’albero dei componenti e trova il provider di context più vicino sopra per quel particolare context.

Per passare il context a un Button, avvolgilo o avvolgi uno dei suoi componenti genitore nel corrispondente provider di context:

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

function Form() {
// ... renders buttons inside ...
}

Non importa quanti livelli di componenti ci siano tra il provider e il Button. Quando un Button ovunque all’interno di Form chiama useContext(ThemeContext), riceverà "dark" come valore.

Insidia

useContext() cerca sempre il provider più vicino sopra il componente che lo chiama. Cerca verso l’alto e non considera i provider nel componente da cui stai chiamando 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="Benvenuto">
      <Button>Registrati</Button>
      <Button>Accedi</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>
  );
}


Aggiornare i dati passati tramite context

Spesso vorrai che il context cambi nel tempo. Per aggiornare il context, combinalo con lo state. Dichiara una variabile di state nel componente genitore e passa lo state corrente come valore del context al provider.

function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Passa al tema chiaro
</Button>
</ThemeContext>
);
}

Ora qualsiasi Button all’interno del provider riceverà il valore corrente di theme. Se chiami setTheme per aggiornare il valore di theme che passi al provider, tutti i componenti Button verranno ri-renderizzati con il nuovo valore 'light'.

Esempi di aggiornamento del context

Esempio 1 di 5:
Aggiornare un valore tramite context

In questo esempio, il componente MyApp contiene una variabile di state che viene poi passata al provider ThemeContext. Selezionare la checkbox “Modalità scura” aggiorna lo state. Cambiare il valore fornito ri-renderizza tutti i componenti che usano quel 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')
          }}
        />
        Usa la modalità scura
      </label>
    </ThemeContext>
  )
}

function Form({ children }) {
  return (
    <Panel title="Benvenuto">
      <Button>Registrati</Button>
      <Button>Accedi</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>
  );
}

Nota che value="dark" passa la stringa "dark", mentre value={theme} passa il valore della variabile JavaScript theme con le parentesi graffe JSX. Le parentesi graffe ti permettono anche di passare valori di context che non sono stringhe.


Specificare un valore predefinito di fallback

Se React non riesce a trovare provider di quel particolare context nell’albero genitore, il valore del context restituito da useContext() sarà uguale al valore predefinito che hai specificato quando hai creato quel context:

const ThemeContext = createContext(null);

Il valore predefinito non cambia mai. Se vuoi aggiornare il context, usalo con lo state come descritto sopra.

Spesso, invece di null, c’è un valore più significativo che puoi usare come predefinito, per esempio:

const ThemeContext = createContext('light');

In questo modo, se renderizzi accidentalmente un componente senza un provider corrispondente, non si romperà. Questo aiuta anche i tuoi componenti a funzionare bene in un ambiente di test senza configurare molti provider nei test.

Nell’esempio sotto, il pulsante “Cambia tema” è sempre chiaro perché si trova al di fuori di qualsiasi provider di context del tema e il valore predefinito del context del tema è 'light'. Prova a modificare il tema predefinito in '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');
      }}>
        Cambia tema
      </Button>
    </>
  )
}

function Form({ children }) {
  return (
    <Panel title="Benvenuto">
      <Button>Registrati</Button>
      <Button>Accedi</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>
  );
}


Sovrascrivere il context per una parte dell’albero

Puoi sovrascrivere il context per una parte dell’albero avvolgendo quella parte in un provider con un valore diverso.

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

Puoi annidare e sovrascrivere i provider quante volte ti serve.

Esempi di sovrascrittura del context

Esempio 1 di 2:
Sovrascrivere un tema

Qui, il pulsante all’interno del Footer riceve un valore di context diverso ("light") rispetto ai pulsanti esterni ("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="Benvenuto">
      <Button>Registrati</Button>
      <Button>Accedi</Button>
      <ThemeContext value="light">
        <Footer />
      </ThemeContext>
    </Panel>
  );
}

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


Ottimizzare le ri-renderizzazioni quando si passano oggetti e funzioni

Puoi passare qualsiasi valore tramite context, inclusi oggetti e funzioni.

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

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

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

Qui, il valore del context è un oggetto JavaScript con due proprietà, una delle quali è una funzione. Ogni volta che MyApp viene ri-renderizzato (per esempio, su un aggiornamento della route), questo sarà un oggetto diverso che punta a una funzione diversa, quindi React dovrà anche ri-renderizzare tutti i componenti in profondità nell’albero che chiamano useContext(AuthContext).

Nelle app più piccole, questo non è un problema. Tuttavia, non c’è bisogno di ri-renderizzarli se i dati sottostanti, come currentUser, non sono cambiati. Per aiutare React a sfruttare questo fatto, puoi avvolgere la funzione login con useCallback e avvolgere la creazione dell’oggetto in useMemo. Questa è un’ottimizzazione delle prestazioni:

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

Come risultato di questa modifica, anche se MyApp deve essere ri-renderizzato, i componenti che chiamano useContext(AuthContext) non dovranno essere ri-renderizzati a meno che currentUser non sia cambiato.

Leggi di più su useMemo e useCallback.


Troubleshooting

Il mio componente non vede il valore del mio provider

Ci sono alcuni modi comuni in cui questo può accadere:

  1. Stai renderizzando <SomeContext> nello stesso componente (o sotto) rispetto a dove stai chiamando useContext(). Sposta <SomeContext> sopra e fuori dal componente che chiama useContext().
  2. Potresti aver dimenticato di avvolgere il tuo componente con <SomeContext>, oppure potresti averlo messo in una parte dell’albero diversa da quella che pensavi. Verifica che la gerarchia sia corretta usando React DevTools.
  3. Potresti imbatterti in un problema di build con il tuo tooling che fa sì che SomeContext visto dal componente che fornisce e SomeContext visto dal componente che legge siano due oggetti diversi. Questo può accadere se usi i symlink, per esempio. Puoi verificarlo assegnandoli a globali come window.SomeContext1 e window.SomeContext2 e poi controllando se window.SomeContext1 === window.SomeContext2 nella console. Se non sono lo stesso oggetto, risolvi il problema a livello del build tool.

Ricevo sempre undefined dal mio context anche se il valore predefinito è diverso

Potresti avere un provider senza value nell’albero:

// 🚩 Non funziona: nessuna prop value
<ThemeContext>
<Button />
</ThemeContext>

Se dimentichi di specificare value, è come passare value={undefined}.

Potresti anche aver usato per errore un nome di prop diverso:

// 🚩 Non funziona: la prop dovrebbe chiamarsi "value"
<ThemeContext theme={theme}>
<Button />
</ThemeContext>

In entrambi i casi dovresti vedere un warning da React nella console. Per risolverli, chiama la prop value:

// ✅ Passare la prop value
<ThemeContext value={theme}>
<Button />
</ThemeContext>

Nota che il valore predefinito dalla tua chiamata createContext(defaultValue) viene usato solo se non c’è alcun provider corrispondente sopra. Se c’è un componente <SomeContext value={undefined}> da qualche parte nell’albero genitore, il componente che chiama useContext(SomeContext) riceverà undefined come valore del context.