Un'introduzione all'API Context di React
Pubblicato: 2022-03-10Per 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 →
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:
- Il primo metodo è tramite
Class.contextType
.
Per utilizzare questo metodo, assegniamo l'oggetto contesto dal nostroThemeContext
alla proprietàcontextType
della nostra classe. Successivamente, saremo in grado di accedere al valore di contesto utilizzandothis.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 assegnatoThemeContext
alla proprietàcontextType
della nostra classe, ho salvato l'oggetto tema corrente nella variabilecurrentTheme
.
Ora prenderemo i colori dalla variabilecurrentTheme
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. - 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 componenteMainWithClass
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 nostroThemeContext
che abbiamo chiamato "tema" e abbiamo preso i valori di colore per quella modalità tema e l'abbiamo assegnato alla variabilecurrentTheme
. 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
- La versione del componente
MainWithClass
che utilizza il metodoClass.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> ); } }
- La versione del componente
MainWithClass
che usa il metodoThemeContext.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