O introducere în API-ul context al lui React

Publicat: 2022-03-10
Rezumat rapid ↬ În acest articol, veți învăța cum să utilizați API-ul Context al React, care vă permite să gestionați stările globale ale aplicațiilor în aplicațiile dvs. React fără a recurge la foraj de recuzită.

Pentru 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 →

Mai multe după săritură! Continuați să citiți mai jos ↓

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ă:

  1. Prima metodă este prin Class.contextType .

    Pentru a folosi această metodă, atribuim obiectul context din contextType proprietății ThemeContext a clasei noastre. După aceea, vom putea accesa valoarea contextului folosind this.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 atribuit ThemeContext proprietății contextType a clasei noastre, am salvat obiectul temei curent în variabila currentTheme .

    Acum, vom lua culorile din variabila currentTheme ș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.
  2. 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 componenta MainWithClass 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ă a ThemeContext pe care am alias-o ca „temă” și am luat valorile de culoare pentru acel mod de temă și am atribuit-o variabilei currentTheme . 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

  1. Versiunea componentei MainWithClass care utilizează metoda 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. Versiunea componentei MainWithClass care utilizează metoda 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;

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