Un'introduzione all'API Context di React

Pubblicato: 2022-03-10
Riepilogo rapido ↬ In questo articolo imparerai come utilizzare l'API Context di React che ti consente di gestire gli stati delle applicazioni globali nelle tue app React senza ricorrere al drill degli oggetti di scena.

Per questo tutorial, dovresti avere una buona conoscenza degli hook. Tuttavia, prima di iniziare, discuterò brevemente cosa sono e gli hook che useremo in questo articolo.

Secondo i React Docs:

“ Gli hook sono una nuova aggiunta in React 16.8. Ti consentono di utilizzare lo stato e altre funzionalità di React senza scrivere una classe.

Questo è fondamentalmente ciò che è un hook React. Ci consente di utilizzare lo stato, i riferimenti e altre funzionalità di React nei nostri componenti funzionali.

Discutiamo dei due hook che incontreremo in questo articolo.

Il gancio useState

L'hook useState ci consente di utilizzare lo stato nei nostri componenti funzionali. Un hook useState prende il valore iniziale del nostro stato come unico argomento e restituisce un array di due elementi. Il primo elemento è la nostra variabile di stato e il secondo elemento è una funzione in cui possiamo utilizzare l'aggiornamento del valore della variabile di stato.

Diamo un'occhiata al seguente esempio:

 import React, {useState} from "react"; function SampleComponent(){ const [count, setCount] = useState(0); }

Qui, count è la nostra variabile di stato e il suo valore iniziale è 0 mentre setCount è una funzione che possiamo usare per aggiornare il valore di count.

L'hook useContext

Ne parlerò più avanti nell'articolo, ma questo hook ci consente sostanzialmente di consumare il valore di un contesto. Ciò che questo significa effettivamente diventerà più evidente più avanti nell'articolo.

Aree di lavoro del filato

Le aree di lavoro Yarn ti consentono di organizzare la base di codice del tuo progetto utilizzando un repository monolitico (monorepo). React è un buon esempio di progetto open source che è monorepo e utilizza gli spazi di lavoro Yarn per raggiungere tale scopo. Leggi un articolo correlato →

Altro dopo il salto! Continua a leggere sotto ↓

Perché abbiamo bisogno dell'API Context?

Vogliamo creare un componente "commutatore di temi" che alterna tra la modalità luce e la modalità oscura per la nostra app React. Ogni componente deve avere accesso alla modalità tema corrente in modo da poter essere stilizzato di conseguenza.

Normalmente, forniremo la modalità tema corrente a tutti i componenti tramite oggetti di scena e aggiorneremo il tema corrente usando state :

 import React from "react"; import ReactDOM from "react-dom"; function App() { return ( <div> <Text theme= "blue" /> <h1>{theme}</h1> </div> ); } function Text({theme}) { return( <h1 style = {{ color: `${theme}` }}>{theme}</h1> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

Nell'esempio di codice sopra, abbiamo creato un componente di testo che esegue il rendering di un elemento h1 . Il colore dell'elemento h1 dipende dalla modalità tema corrente. Attualmente, il tema è blu. Possiamo alternare tra i temi blue e red usando state .

Creeremo uno stato chiamato "tema" usando l'hook useState . L'hook useState restituirà il valore corrente del tema e una funzione che possiamo usare per aggiornare il tema.

Quindi, creiamo il nostro stato tema:

 const [theme, setTheme] = React.useState("blue");

Aggiungeremo anche un elemento pulsante al nostro componente App . Questo pulsante verrà utilizzato per alternare i temi e necessita di un gestore di eventi clic. Quindi, scriviamo il gestore dell'evento click in questo modo:

 const onClickHandler = () => { setTheme(); }

Ora, vogliamo impostare il nuovo tema su Red se il tema corrente è Blue e viceversa. Invece di usare un'istruzione if , un modo più conveniente per farlo è con l'aiuto dell'operatore ternario in JavaScript.

 setTheme( theme === "red"? "blue": "red");

Quindi ora abbiamo scritto il nostro gestore onClick . Aggiungiamo questo elemento pulsante al componente App :

 <button onClick = {onClickHandler}>Change theme</button>

Cambiamo anche il valore degli oggetti di scena del componente Testo nello stato del tema.

 <Text theme={theme}/>

Ora, dovremmo avere questo:

 import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { const[theme, setTheme] = React.useState("red"); const onClickHandler = () => { setTheme( theme === "red"? "blue": "red"); } return ( <div> <Text theme={theme}/> <button onClick = {onClickHandler}>Change theme</button> </div> ); } function Text({theme}) { return( <h1 style = {{ color: `${theme}` }}>{theme}</h1> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

Ora possiamo alternare tra i nostri due temi. Tuttavia, se si trattasse di un'applicazione molto più ampia, sarebbe difficile utilizzare il tema in componenti profondamente nidificati e il codice diventa ingombrante.

Presentazione dell'API Context

Consentitemi di introdurre l'API Context. Secondo la documentazione React:

"Il contesto fornisce un modo per passare i dati attraverso l'albero dei componenti senza dover passare manualmente gli oggetti di scena a tutti i livelli."

Per una definizione più approfondita, fornisce un modo per rendere disponibili dati particolari a tutti i componenti nell'albero dei componenti, indipendentemente da quanto profondamente nidificato possa essere quel componente.

Diamo un'occhiata a questo esempio:

 const App = () => { return( <ParentComponent theme = "light"/> ); } const ParentComponent = (props) => ( <Child theme = {props.theme} /> ) const Child = (props) => ( <Grandchild theme = {props.theme} /> ) const Grandchild = (props) => ( <p>Theme: {props.theme}</p> )

Nell'esempio sopra, abbiamo specificato il tema dell'applicazione usando un props nel ParentComponent chiamato theme . Abbiamo dovuto passare quegli oggetti di scena a tutti i componenti lungo l'albero dei componenti per portarli dove è necessario, ovvero il componente GrandChild . Il ChildComponent non aveva nulla a che fare con gli oggetti di scena del tema ma era solo usato come intermediario.

Ora, immagina che il componente GrandChild fosse nidificato più profondamente di quanto non fosse nell'esempio in alto. Dovremmo passare gli oggetti di scena del tema nello stesso modo in cui abbiamo fatto qui, il che sarebbe ingombrante. Questo è il problema che Context risolve. Con Context , ogni componente nell'albero dei componenti ha accesso a tutti i dati che decidiamo di inserire nel nostro contesto.

Iniziamo con il Context

È ora di replicare il pulsante di attivazione/disattivazione del tema che abbiamo creato all'inizio dell'articolo con l'API Context. Questa volta, il nostro commutatore di temi sarà un componente separato. Costruiremo un componente ThemeToggler che cambia il tema della nostra app React usando Context .

Innanzitutto, inizializziamo la nostra app React. (Preferisco usare create-react-app ma puoi usare qualsiasi metodo tu preferisca.)

Dopo aver inizializzato il tuo progetto React, crea un file chiamato ThemeContext.js nella tua cartella /src . Puoi anche creare una cartella chiamata /context e inserire il tuo file ThemeContext se lo desideri.

Ora, andiamo avanti.

Creazione della tua API di contesto

Creeremo il nostro contesto del tema nel nostro file ThemeContext.js .

Per creare un contesto, utilizziamo React.createContext che crea un oggetto contesto. Puoi passare qualsiasi cosa come argomento a React.createContext . In questo caso, passeremo una stringa che è la modalità tema corrente. Quindi ora la nostra modalità tema attuale è la modalità tema "leggera".

 import React from "react"; const ThemeContext = React.createContext("light"); export default ThemeContext;

Per rendere questo contesto disponibile a tutti i nostri componenti React, dobbiamo utilizzare un Provider. Che cos'è un fornitore? Secondo la documentazione di React, ogni oggetto di contesto viene fornito con un componente Provider React che consente ai componenti di consumo di sottoscrivere le modifiche al contesto. È il provider che consente al contesto di essere utilizzato da altri componenti. Detto questo, creiamo il nostro provider.

Vai al tuo file App.js. Per creare il nostro provider, dobbiamo importare il nostro ThemeContext .

Una volta importato il ThemeContext , dobbiamo racchiudere il contenuto del nostro componente App nei tag ThemeContext.Provider e assegnare al componente ThemeContext.Provider un prop chiamato value che conterrà i dati che vogliamo rendere disponibili al nostro albero dei componenti.

 function App() { const theme = "light"; return ( <ThemeContext.Provider value = {theme}> <div> </div> </ThemeContext.Provider> ); }

Quindi ora il valore di “luce” è a disposizione di tutti i nostri componenti (che scriveremo presto).

Creazione del nostro file del tema

Ora creeremo il nostro file del tema che conterrà i diversi valori di colore per i nostri temi chiari e scuri. Crea un file nella tua cartella /src chiamato Colors.js .

In Colors.js creeremo un oggetto chiamato AppTheme . Questo oggetto conterrà i colori per i nostri temi. Al termine, esporta l'oggetto AppTheme in questo modo:

 const AppTheme = { light: { textColor: "#000", backgroundColor: "#fff" }, dark: { textColor: "#fff", backgroundColor: "#333" } } export default AppTheme;

Ora è il momento di iniziare a creare i nostri diversi componenti React.

Creazione dei nostri componenti React

Creiamo i seguenti componenti:

  • Header
  • ThemeToggler
  • MainWithClass

Header.jsx

 import React from "react"; import ThemeToggler from "./ThemeToggler"; const headerStyles = { padding: "1rem", display: "flex", justifyContent: "space-between", alignItems: "center" } const Header = () => { return( <header style = {headerStyles}> <h1>Context API</h1> <ThemeToggler /> </header> ); } export default Header;

ThemeToggler.jsx

(Per ora, restituiremo solo un div vuoto.)

 import React from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { return( <div style = {themeTogglerStyle}> </div> ); } export default ThemeToggler;

Utilizzo del contesto con componenti basati su classi

Qui useremo il valore del nostro ThemeContext . Come forse già saprai, abbiamo due metodi per scrivere componenti in React : tramite funzioni o classi. Il processo di utilizzo del contesto in entrambi i metodi è diverso, quindi creeremo due componenti che fungeranno da sezione principale della nostra applicazione: MainWithClass e MainWithFunction .

Cominciamo con MainWithClass .

MainWithClass.jsx

Dovremo importare il nostro ThemeContext e AppTheme . Una volta fatto, scriveremo una classe che restituisce il nostro JSX da un metodo di rendering. Ora dobbiamo consumare il nostro contesto. Esistono due metodi per farlo con i componenti basati sulla classe:

  1. Il primo metodo è tramite Class.contextType .

    Per utilizzare questo metodo, assegniamo l'oggetto contesto dal nostro ThemeContext alla proprietà contextType della nostra classe. Successivamente, saremo in grado di accedere al valore di contesto utilizzando this.context . Puoi anche fare riferimento a questo in qualsiasi metodo del ciclo di vita e persino nel metodo di rendering.

     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context]; return( <main></main> ); } }

    Dopo aver assegnato ThemeContext alla proprietà contextType della nostra classe, ho salvato l'oggetto tema corrente nella variabile currentTheme .

    Ora prenderemo i colori dalla variabile currentTheme e li useremo per dare uno stile al markup.
     render() { const currentTheme = AppTheme[this.context]; return ( <main style={{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main>

    Questo è tutto! Questo metodo, tuttavia, ti limita a utilizzare un solo contesto.
  2. Il secondo metodo è ThemeContext.Consumer che prevede l'uso di un Consumer. Ogni oggetto di contesto viene inoltre fornito con un componente Consumer React che può essere utilizzato in un componente basato su classi. Il componente consumer accetta un figlio come funzione e quella funzione restituisce un nodo React. Il valore di contesto corrente viene passato a quella funzione come argomento.

    Ora sostituiamo il codice nel nostro componente MainWithClass con questo:
     class Main extends Component { constructor() { super(); this.state = { } } render(){ return( <ThemeContext.Consumer> { (theme) => { const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ) } } </ThemeContext.Consumer> ); } }

    Come puoi vedere, abbiamo usato il valore corrente del nostro ThemeContext che abbiamo chiamato "tema" e abbiamo preso i valori di colore per quella modalità tema e l'abbiamo assegnato alla variabile currentTheme . Con questo metodo puoi utilizzare più consumatori.

Questi sono i due metodi per consumare il contesto con i componenti basati sulla classe.

Consumo di contesto con componenti funzionali

Consumare il contesto con i componenti funzionali è più facile e meno noioso che farlo con i componenti basati su classi. Per consumare il contesto in un componente funzionale, useremo un hook chiamato useContext .

Ecco come sarebbe il consumo del nostro ThemeContext con un componente funzionale:

 const Main = () => { const theme = useContext(ThemeContext); const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } export default Main;

Come puoi vedere, tutto ciò che dovevamo fare era usare il nostro hook useContext con il nostro ThemeContext passato come argomento.

Nota : devi utilizzare questi diversi componenti nel file App.js per vedere i risultati.

Aggiornare il nostro tema con il componente ThemeToggler

Ora lavoreremo sul nostro componente ThemeToggler . Dobbiamo essere in grado di alternare tra i temi chiari e scuri. Per fare ciò, dovremo modificare il nostro ThemeContext.js . Il nostro React.createContext ora prenderà un oggetto simile al risultato di un hook useState come argomento.

 const ThemeContext = React.createContext(["light", () => {}]);

Abbiamo passato un array alla funzione React.createContext . Il primo elemento nell'array è la modalità tema corrente e il secondo elemento è la funzione che verrebbe utilizzata per aggiornare il tema. Come ho detto, questo assomiglia semplicemente al risultato di un hook useState ma non è esattamente il risultato di un hook useState .

Ora modificheremo il nostro file App.js. È necessario modificare il valore passato al provider in un hook useState . Ora il valore del nostro Contesto Tema è un hook useState il cui valore predefinito è "light".

 function App() { const themeHook = useState("light"); return ( <ThemeContext.Provider value = {themeHook}> <div> <Header /> <Main /> </div> </ThemeContext.Provider> ); }

Scrivere il nostro componente ThemeToggler

Ora scriviamo effettivamente il nostro componente ThemeToggler :

 import React,{useContext} from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { const[themeMode, setThemeMode] = useContext(ThemeContext); return( <div style = {themeTogglerStyle} onClick = {() => {setThemeMode(themeMode === "light"? "dark": "light")}}> <span title = "switch theme"> {themeMode === "light" ? "" : "️"} </span> </div> ); } export default ThemeToggler;

Poiché il valore del contesto del nostro tema ora è un hook ogni volta che chiamiamo useContext su di esso, restituirà un array. Usando la destrutturazione, siamo stati in grado di catturare gli elementi dall'array. Abbiamo quindi scritto un gestore di eventi onClick per il nostro ThemeToggler . Con quel codice, ogni volta che si fa clic sull'interruttore del tema, cambierà il tema della nostra applicazione.

Ora modificheremo le diverse versioni del nostro componente Main .

Modifica del nostro componente MainWithClass

  1. La versione del componente MainWithClass che utilizza il metodo Class.contextType :
     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context[0]]; return( <main style={{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } }
  2. La versione del componente MainWithClass che usa il metodo ThemeContext.Consumer :
     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component { constructor() { super(); this.state = {} } render() { return ( <ThemeContext.Consumer> { ([theme]) => { const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ) } } </ThemeContext.Consumer> ); } } export default Main;

Modifica del nostro componente MainWithFunction

Il componente MainWithFunction deve essere modificato come segue:

 import React, { useContext } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; const Main = () => { const theme = useContext(ThemeContext)[0]; const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } export default Main;

Conclusione

Questo è tutto! Siamo riusciti a implementare due modalità del tema per la nostra app React utilizzando l'API Context.

Nel processo, abbiamo imparato:

  • Che cos'è l'API Context e il problema che risolve;
  • Quando utilizzare l'API Context;
  • Creare il Context e consumarlo in componenti sia funzionali che basati su classi.

Ulteriori letture su SmashingMag:

  • Styling nelle moderne app Web
  • Creazione di app mobili con Ionic e React
  • Crea una PWA con Webpack e Workbox
  • Conoscere l'API MutationObserver