O introducere în API-ul context al lui React
Publicat: 2022-03-10Pentru acest tutorial, ar trebui să aveți o înțelegere corectă a cârligelor. Totuși, înainte de a începe, voi discuta pe scurt ce sunt acestea și cârligele pe care le vom folosi în acest articol.
Potrivit React Docs:
„ Cârligele sunt o nouă adăugare în React 16.8. Vă permit să utilizați stare și alte funcții React fără a scrie o clasă.”
Practic, asta este un cârlig React. Ne permite să folosim state, refs și alte caracteristici React în componentele noastre funcționale.
Să discutăm cele două cârlige pe care le vom întâlni în acest articol.
useState
Hook
Cârligul useState ne permite să folosim starea în componentele noastre funcționale. Un cârlig useState
ia valoarea inițială a stării noastre ca singur argument și returnează o matrice de două elemente. Primul element este variabila noastră de stare, iar al doilea element este o funcție în care putem folosi actualizarea valorii variabilei de stare.
Să aruncăm o privire la următorul exemplu:
import React, {useState} from "react"; function SampleComponent(){ const [count, setCount] = useState(0); }
Aici, count
este variabila noastră de stare și valoarea sa inițială este 0
, în timp ce setCount
este o funcție pe care o putem folosi pentru a actualiza valoarea count.
useContext
Hook
Voi discuta despre asta mai târziu în articol, dar acest cârlig ne permite practic să consumăm valoarea unui context. Ce înseamnă asta de fapt va deveni mai evident mai târziu în articol.
Spații de lucru cu fire
Spațiile de lucru Yarn vă permit să vă organizați baza de cod a proiectului folosind un depozit monolitic (monorepo). React este un bun exemplu de proiect open-source care este monorepo și utilizează spații de lucru Yarn pentru a atinge acest scop. Citiți un articol înrudit →
De ce avem nevoie de API-ul Context?
Dorim să construim o componentă „toggler theme” care comută între modul lumină și modul întunecat pentru aplicația noastră React. Fiecare componentă trebuie să aibă acces la modul actual al temei, astfel încât să poată fi stilate corespunzător.
În mod normal, am furniza modul actual al temei tuturor componentelor prin elemente de recuzită și am actualiza tema curentă folosind 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);
În exemplul de cod de mai sus, am creat o componentă text care redă un element h1
. Culoarea elementului h1
depinde de modul actual al temei. În prezent, tema este albastră. Putem comuta între temele blue
și red
folosind state
.
Vom crea o stare numită „temă” folosind cârligul useState
. Cârligul useState
va returna valoarea curentă a temei și o funcție pe care o putem folosi pentru a actualiza tema.
Deci, haideți să ne creăm starea temei:
const [theme, setTheme] = React.useState("blue");
Vom adăuga, de asemenea, un element buton la componenta noastră App
. Acest buton va fi folosit pentru a comuta între teme și are nevoie de un handler de evenimente de clic. Așadar, haideți să scriem gestionarea evenimentelor de clic astfel:
const onClickHandler = () => { setTheme(); }
Acum, vrem să setăm noua temă la Red
dacă tema curentă este Blue
și invers. În loc să folosiți o instrucțiune if
, o modalitate mai convenabilă de a face acest lucru este cu ajutorul operatorului ternar din JavaScript.
setTheme( theme === "red"? "blue": "red");
Așa că acum, am scris handlerul nostru onClick
. Să adăugăm acest element de buton la componenta App
:
<button onClick = {onClickHandler}>Change theme</button>
Să modificăm și valoarea elementelor de recuzită a temei componentei Text în starea temei.
<Text theme={theme}/>
Acum, ar trebui să avem asta:
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);
Acum putem comuta între cele două teme ale noastre. Cu toate acestea, dacă aceasta ar fi o aplicație mult mai mare, ar fi dificil să utilizați tema în componente profund imbricate și codul devine greu de utilizat.
Vă prezentăm API-ul Context
Permiteți-mi să vă prezint API-ul Context. Conform documentației React:
„Contextul oferă o modalitate de a trece date prin arborele componente fără a fi nevoie să transmiteți recuzita manual la fiecare nivel.”
Pentru o definiție mai aprofundată, vă oferă o modalitate de a face anumite date disponibile pentru toate componentele din arborele de componente, indiferent cât de adânc ar fi imbricată acea componentă.
Să ne uităm la acest exemplu:
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> )
În exemplul de mai sus, am specificat tema aplicației folosind o recuzită în ParentComponent
numită theme
. A trebuit să transmitem acele elemente de recuzită tuturor componentelor din arborele de componente pentru a le ajunge acolo unde este nevoie, adică componenta GrandChild
. ChildComponent
nu a avut nimic de-a face cu elementele de recuzită a temei, ci a fost folosit doar ca intermediar.
Acum, imaginați-vă că componenta GrandChild
era mai profund imbricată decât era în exemplul de sus. Ar trebui să trecem recuzita temei în același mod în care am făcut-o aici, ceea ce ar fi greoi. Aceasta este problema pe care Context
o rezolvă. Cu Context
, fiecare componentă din arborele de componente are acces la orice date pe care decidem să le punem în contextul nostru.
Să începem cu Context
Este timpul să reproducem butonul de comutare a temei pe care l-am creat la începutul articolului cu API-ul Context. De data aceasta, comutatorul nostru de teme va fi o componentă separată. Vom construi o componentă ThemeToggler
care schimbă tema aplicației noastre React folosind Context
.
Mai întâi, să inițializam aplicația noastră React. (Prefer să folosesc create-react-app
dar puteți folosi orice metodă doriți.)
După ce ați inițializat proiectul React, creați un fișier numit ThemeContext.js în folderul /src
. De asemenea, puteți crea un folder numit /context
și plasați fișierul ThemeContext acolo dacă doriți.
Acum, hai să mergem mai departe.
Crearea API-ului dvs. de context
Ne vom crea contextul temei în fișierul nostru ThemeContext.js .
Pentru a crea un context, folosim React.createContext
care creează un obiect context. Puteți trece orice ca argument pentru React.createContext
. În acest caz, vom trece într-un șir care este modul actual al temei. Așa că acum modul nostru de temă actual este modul temă „luminoasă”.
import React from "react"; const ThemeContext = React.createContext("light"); export default ThemeContext;
Pentru a face acest context disponibil tuturor componentelor noastre React, trebuie să folosim un Furnizor. Ce este un Furnizor? Conform documentației React, fiecare obiect de context vine cu o componentă Provider React care permite componentelor consumatoare să se aboneze la schimbările de context. Este furnizorul care permite ca contextul să fie consumat de alte componente. Acestea fiind spuse, haideți să ne creăm furnizorul.
Accesați fișierul App.js. Pentru a ne crea furnizorul, trebuie să importam ThemeContext
.
Odată ce ThemeContext
a fost importat, trebuie să includem conținutul componentei noastre App
în etichetele ThemeContext.Provider
și să dăm componentei ThemeContext.Provider
o recuzită numită value
care va conține datele pe care dorim să le punem la dispoziție arborelui nostru de componente.
function App() { const theme = "light"; return ( <ThemeContext.Provider value = {theme}> <div> </div> </ThemeContext.Provider> ); }
Deci acum valoarea „luminii” este disponibilă pentru toate componentele noastre (pe care le vom scrie în curând).
Crearea fișierului nostru temă
Acum, vom crea fișierul nostru temă care va conține diferitele valori de culoare atât pentru temele noastre deschise, cât și pentru cele întunecate. Creați un fișier în folderul /src
numit Colors.js .
În Colors.js , vom crea un obiect numit AppTheme
. Acest obiect va conține culorile pentru temele noastre. După ce ați terminat, exportați obiectul AppTheme
astfel:
const AppTheme = { light: { textColor: "#000", backgroundColor: "#fff" }, dark: { textColor: "#fff", backgroundColor: "#333" } } export default AppTheme;
Acum este timpul să începem să creăm diferitele noastre componente React.
Crearea componentelor noastre React
Să creăm următoarele componente:
-
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
(Deocamdată, vom returna doar un div
gol.)
import React from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { return( <div style = {themeTogglerStyle}> </div> ); } export default ThemeToggler;
Consumul de context cu componente bazate pe clasă
Aici, vom folosi valoarea ThemeContext
. După cum probabil știți deja, avem două metode de scriere a componentelor în React : prin funcții sau clase. Procesul de utilizare a contextului în ambele metode este diferit, așa că vom crea două componente care să servească drept secțiune principală a aplicației noastre: MainWithClass
și MainWithFunction
.
Să începem cu MainWithClass
.
MainWithClass.jsx
Va trebui să importam ThemeContext
și AppTheme
. Odată ce este făcut, vom scrie o clasă care returnează JSX-ul nostru dintr-o metodă de randare. Acum trebuie să ne consumăm contextul. Există două metode pentru a face acest lucru cu componente bazate pe clasă:
- Prima metodă este prin
Class.contextType
.
Pentru a folosi această metodă, atribuim obiectul context dincontextType
proprietățiiThemeContext
a clasei noastre. După aceea, vom putea accesa valoarea contextului folosindthis.context
. De asemenea, puteți face referire la aceasta în oricare dintre metodele ciclului de viață și chiar în metoda de randare.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> ); } }
După ce am atribuitThemeContext
proprietățiicontextType
a clasei noastre, am salvat obiectul temei curent în variabilacurrentTheme
.
Acum, vom lua culorile din variabilacurrentTheme
și le vom folosi pentru a stila unele marcaje.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>
Asta e! Această metodă, însă, te limitează la consumarea unui singur context. - A doua metodă este
ThemeContext.Consumer
care implică utilizarea unui Consumator. Fiecare obiect context vine, de asemenea, cu o componentă Consumer React care poate fi utilizată într-o componentă bazată pe clasă. Componenta consumer ia un copil ca funcție și acea funcție returnează un nod React. Valoarea actuală a contextului este transmisă acelei funcții ca argument.
Acum, să înlocuim codul din componentaMainWithClass
cu acesta: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> ); } }
După cum puteți vedea, am folosit valoarea curentă aThemeContext
pe care am alias-o ca „temă” și am luat valorile de culoare pentru acel mod de temă și am atribuit-o variabileicurrentTheme
. Cu această metodă, puteți utiliza mai mulți consumatori.
Acestea sunt cele două metode de a consuma context cu componente bazate pe clasă.
Consumul de context cu componente funcționale
Consumul de context cu componente funcționale este mai ușor și mai puțin obositor decât a face acest lucru cu componente bazate pe clasă. Pentru a consuma context într-o componentă funcțională, vom folosi un cârlig numit useContext
.
Iată cum ar arăta consumarea ThemeContext
cu o componentă funcțională:
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;
După cum puteți vedea, tot ce trebuia să facem a fost să folosim cârligul nostru useContext
cu ThemeContext
transmis ca argument.
Notă : trebuie să utilizați aceste componente diferite în fișierul App.js pentru a vedea rezultatele.
Actualizarea temei noastre cu componenta ThemeToggler
Acum vom lucra la componenta noastră ThemeToggler
. Trebuie să putem comuta între temele luminoase și cele întunecate. Pentru a face acest lucru, va trebui să ne edităm ThemeContext.js . React.createContext
va lua acum un obiect asemănător cu rezultatul unui hook useState
ca argument.
const ThemeContext = React.createContext(["light", () => {}]);
Am transmis o matrice funcției React.createContext
. Primul element din matrice este modul actual al temei, iar al doilea element este funcția care ar fi utilizată pentru a actualiza tema. După cum am spus, acesta seamănă doar cu rezultatul unui hook useState
, dar nu este exact rezultatul unui hook useState
.
Acum vom edita fișierul App.js. Trebuie să schimbăm valoarea transmisă furnizorului la un hook useState
. Acum valoarea contextului nostru teme este un cârlig useState
a cărui valoare implicită este „light”.
function App() { const themeHook = useState("light"); return ( <ThemeContext.Provider value = {themeHook}> <div> <Header /> <Main /> </div> </ThemeContext.Provider> ); }
Scrierea componentei noastre ThemeToggler
Să scriem acum componenta noastră 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;
Deoarece valoarea contextului temei noastre este acum un cârlig de fiecare dată când apelăm useContext
pe el, va returna o matrice. Folosind destructurarea, am reușit să luăm elementele din matrice. Apoi am scris un handler de evenimente onClick
pentru ThemeToggler
. Cu acel cod, ori de câte ori se face clic pe butonul de comutare a temei, acesta va schimba tema aplicației noastre.
Acum vom edita diferitele versiuni ale componentei noastre Main
.
Editarea componentei noastre MainWithClass
- Versiunea componentei
MainWithClass
care utilizează metodaClass.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> ); } }
- Versiunea componentei
MainWithClass
care utilizează metodaThemeContext.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;
Editarea componentei noastre MainWithFunction
Componenta MainWithFunction
ar trebui editată după cum urmează:
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;
Concluzie
Asta e! Am reușit să implementăm două moduri tematice pentru aplicația noastră React folosind API-ul Context.
În acest proces, am învățat:
- Ce este Context API și problema pe care o rezolvă;
- Când să utilizați API-ul Context;
- Crearea
Context
și consumarea acestuia atât în componente funcționale, cât și bazate pe clasă.
Citiți suplimentare despre SmashingMag:
- Stilizarea în aplicațiile web moderne
- Crearea de aplicații mobile cu Ionic și React
- Construiește un PWA cu Webpack și Workbox
- Cunoașterea API-ului MutationObserver