Utilizzare il context per passare dati a componenti innestati
"Come usare il Context per passare dati e funzioni ai componenti innestati senza usare le props."

Troverai nel progetto
Passere le informazioni da un componente genitore a un componente figlio tramite le props può diventare difficile da mantenere se devi passarle attraverso molti componenti nel mezzo o se molti componenti devono accedere alle stesse informazioni.
Il context consente al componente genitore di rendere disponibili alcune informazioni a qualsiasi componente nell'albero sottostante, non importa quanto sia profondo, senza passarle esplicitamente le props.
Il problema del passaggio delle props
Il modo più comune di condividere le proprietà tra componenti è quello di passare delle props dal componente genitore al componente figlio. E fin qui niente di strano.
Ma immaginiamo una situazione abbastanza comune: un componente innestato profondamente, magari in 3-4 componenti, avesse bisogno di accedere a queste props. Ci troveremmo a dover passare meccanicamente delle props attraverso 3-4 componenti.
Un altro scenario problematico potrebbe essere banalmente una decina di componenti, anche di primo livello, che necessitano di accedere tutti alla stessa informazione. Questo pattern si chiama props drilling anche detto "bye bye manutenibilità del codice".

Context: un'alternativa al passaggio delle props
Non Sarebbe bello se potessimo "teletrasportare" le nostre props nell'albero dei nostri componenti esattamente dove ci servono ? Beh in effetti possiamo. Il context permette ad un componente genitore di passare le proprie proprietà a tutti i figli.
Esempio di prop drilling
In questo esempio cercheremo di mostrare un heading differente h1, h2, h3 etc.. a seconda di quanto sia innestato il componente. In questo modo avremmo:
- Un albero di componenti molto profondo
- Molti componenti che devono accedere alla stessa informazione.
function Heading({ level, children }) { switch (level) { case 1: return <h1>{children}</h1>; case 2: return <h2>{children}</h2>; case 3: return <h3>{children}</h3>; case 4: return <h4>{children}</h4>; case 5: return <h5>{children}</h5>; case 6: return <h6>{children}</h6>; default: throw Error('Unknown level: ' + level); } } function Section({ children }) { return ( <section className="section"> {children} </section> ); } export default function Page() { return ( <Section> <Heading level={1}>Title</Heading> <Heading level={2}>Heading</Heading> <Heading level={3}>Sub-heading</Heading> <Heading level={4}>Sub-sub-heading</Heading> <Heading level={5}>Sub-sub-sub-heading</Heading> <Heading level={6}>Sub-sub-sub-sub-heading</Heading> </Section> ); }
Come vedete al momento siamo costretti a passare ad ogni singolo Heading il suo livello. Ma noi vorremmo passare ad ogni section il suo livello e far si che i rispettivi Heading si comportino in modo corretto.
<Section level={3}> <Heading>About</Heading> <Heading>Photos</Heading> <Heading>Videos</Heading> </Section>
Ma come possono i nostri Heading components ricavare il loro livello dalla section ? Avremmo bisogno di un qualche modo di far si che un figlio posso chiedere dei dati da "qualcuno" più in altro nell'albero dei componenti

Ed ecco uno perfetto scenario per il nostro context. Non dobbiamo fare altro che:
- Creare un context: che chiameremo LevelContext
- Usare il context: Il componente Heading userà il LevelContext per richiedere le informazioni di cui ha bisogno
- Provvedere_il context: il componente Section fornirà le informazioni.
Step 1: Come creare un context
Creiamo un file LevelContext.js
import { createContext } from 'react'; export const LevelContext = createContext(1);
Tutto qua ? Si, l'unico requisito per creare un nuovo context è fornire un valore di default (nel nostro caso 1). Ovviamente possiamo passare valori più complessi come un oggetto al nostro context.
Step 2: Usare il context (useContext)
Nel nostro file principale andiamo ad importare da React il useContext Hook e il nostro LevelContext
import { useContext } from 'react'; import { LevelContext } from './LevelContext.js';
A questo punto modifichiamo il nostro Heading Component rimuovendo la props level, che ricaveremo usando il nostro context
export default function Heading({ children }) { const level = useContext(LevelContext); // ... }
Lo useContext è un Hook come lo useState e lo useEffect e come essi deve essere usato all'inizio del nostro component. In questo caso lo useContext sta dicendo a React che il nostro Heading component vuole leggere il LevelContext
A questo punto visto che il nostro Heading component non accetta più la props di level non abbiamo più bisogno di passargliela e ora possiamo riscrivere il nostro page component così.
<Section level={3}> <Heading>About</Heading> <Heading>Photos</Heading> <Heading>Videos</Heading> </Section>
Step 3: Provvedere un Context (Context Provider)
In questo momento il nostro esempio non funziona perché il nostro Page component ritornerà tutti h1. Questo perché abbiamo indicato chi userà il context non chi provvederà il nostro context
Quando non si fornisce un context React utilizzerà il valore di default che gli è stato fornito e siccome nel nostro esempio abbiamo passato 1 come valore di default al nostro createContext(), il nostro useContext(LevelContext) ritornerà 1. Ora andiamo a fixare il problema.
import { LevelContext } from './LevelContext.js'; export default function Section({ level, children }) { return ( <section className="section"> <LevelContext.Provider value={level}> {children} </LevelContext.Provider> </section> ); }
Come possiamo vedere il nostro LevelContext.Provider accetta una props value a cui andiamo a passare il valore di level che la nostra section accetta come props. In questo modo tutti gli Heading components all'interno di quella section potranno richiedere l'accesso e leggere il valore del livello.
Ed il nostro esempio finale ora funziona e ci permette di:
- Passare il livello alla nostra section come Props
- La nostra section fungerà da provider del nostro Context (LevelContext.Provider)
- Il nostri Heading potranno accedere al livello della section che li wrappa.
Risultato finale
function Page() { return ( <Section level={1}> <Heading>Title</Heading> <Section level={2}> <Heading>Heading</Heading> <Heading>Heading</Heading> <Heading>Heading</Heading> <Section level={3}> <Heading>Sub-heading</Heading> <Heading>Sub-heading</Heading> <Heading>Sub-heading</Heading> <Section level={4}> <Heading>Sub-sub-heading</Heading> <Heading>Sub-sub-heading</Heading> <Heading>Sub-sub-heading</Heading> </Section> </Section> </Section> </Section> ); }