Context API-ul React a devenit instrumentul de gestionare a statului la alegere pentru mulți, de multe ori înlocuind cu totul Redux. În acest tutorial rapid de 5 minute, veți vedea o introducere a ceea ce este contextul și cum să-l utilizați!

Dacă doriți o introducere corectă a acestui subiect, vă puteți alătura listei de așteptare pentru viitoare curs React avansat, sau dacă sunteți încă un începător, verificați-mi curs introductiv gratuit despre React.

Luați în considerare acest arbore, în care casetele de jos reprezintă componente separate:

Arborele componentelor

Putem adăuga cu ușurință starea la componentele inferioare, dar până acum singura modalitate de a transmite date fratelui unei componente a fost mutarea stării la o componentă superioară și apoi transmiterea acesteia înapoi către fratele său prin recuzită.

Transmiterea datelor prin recuzită

Dacă vom afla mai târziu că fratele componentei cu stat are nevoie și de date, trebuie să ridicăm din nou starea și să o transmitem înapoi:

Trecerea stării în jos prin mai multe niveluri

În timp ce această soluție funcționează, problemele încep dacă o componentă dintr-o ramură diferită are nevoie de date:

Componenta mai îndepărtată necesită date

În acest caz, trebuie să trecem starea de la nivelul superior al aplicației prin toate componentele intermediare la cea care are nevoie de date în partea de jos, chiar dacă nivelurile intermediare nu au nevoie de ele. Acest proces obositor și consumator de timp este cunoscut sub numele de foraj prop.

Foraj de prop

Aici intervine Context API. Oferă o modalitate de a transmite date prin arborele componentelor printr-o pereche Furnizor-Consumator, fără a fi nevoie să transmiteți recuzită prin fiecare nivel. Gândiți-vă la ele ca la componentele care joacă Catch cu date – componentele intermediare s-ar putea să nu „știe” niciodată că se întâmplă ceva:

Contextul în acțiune

Pentru a demonstra acest lucru, vom crea această imagine de comutare de zi cu noapte funky (și super utilă).

3evdww

Dacă doriți să vedeți codul complet, asigurați-vă că verificați locul de joacă Scrimba pentru acest articol.

Creați context

Pentru început, creăm un nou context. Deoarece vrem ca întreaga aplicație să aibă acces la aceasta, mergem la index.js și înfășurați aplicația ThemeContext.Provider.

Trecem și de value sprijin pentru Furnizorul nostru. Aceasta conține datele pe care vrem să le salvăm. Deocamdată, am introdus codul hard 'Day'.

import React from "react";
import ReactDOM from "react-dom";
import ThemeContext from "./themeContext";

import App from "./App";

ReactDOM.render(
  <ThemeContext.Provider value={"Day"}>
    <App />
  </ThemeContext.Provider>,
  document.getElementById("root")
);

Consumarea contextului cu contextType

În prezent, în App.js, ne întoarcem pur și simplu <Image /> componentă.

import React from "react";
import Image from "./Image";

class App extends React.Component {
  render() {
    return (
      <div className="app">
        <Image />
      </div>
    );
  }
}

export default App;

Scopul nostru este să folosim Context pentru a schimba clasele Image.js din Day la Night, în funcție de ce imagine dorim să redăm. Pentru a face acest lucru, adăugăm o proprietate statică la componenta noastră numită ContextType și apoi utilizați interpolare șir pentru a o adăuga la classNames în <Image /> componentă.

Acum, classNames conține șirul din value recuzită. Notă: m-am mutat ThemeContext în propriul fișier pentru a preveni o eroare.

import React from "react";
import Button from "./Button";
import ThemeContext from "./themeContext";

class Image extends React.Component {
  render() {
    const theme = this.context;
    return (
      <div className={`${theme}-image image`}>
        <div className={`${theme}-ball ball`} />
        <Button />
      </div>
    );
  }
}

Image.contextType = ThemeContext;

export default Image;

Context.Consumator

Din păcate, această abordare funcționează numai cu componente bazate pe clase. Dacă ați aflat deja despre Hooks în React, veți ști că putem face aproape orice cu componente funcționale în aceste zile. Deci, pentru o bună măsură, ar trebui să ne convertim componentele în componente funcționale și apoi să le folosim ThemeContext.Consumer componentă pentru a transmite informații prin aplicație.

Acest lucru se face prin împachetarea elementelor noastre într-o instanță de <ThemeContext.Consumer> și în interiorul căruia (unde children go), oferind o funcție care returnează elementele. Acesta folosește modelul „render prop” unde oferim o funcție obișnuită ca un copil care returnează unele JSX pentru a reda.

import React from "react";
import Button from "./Button";
import ThemeContext from "./themeContext";

function Image(props) {
  // We don't need this anymore
  // const theme = this.context
  
  return (
    <ThemeContext.Consumer>
      {theme => (
        <div className={`${theme}-image image`}>
          <div className={`${theme}-ball ball`} />
          <Button />
        </div>
      )}
    </ThemeContext.Consumer>
  );
}

// We don't need this anymore
// Image.contextType = ThemeContext;

export default Image;

Notă: De asemenea, trebuie să înfășurăm <Button /> componentă în <ThemeContext.Consumer> – acest lucru ne permite să adăugăm funcționalitate butonului mai târziu.

import React from "react";
import ThemeContext from "./themeContext";

function Button(props) {
  return (
    <ThemeContext.Consumer>
      {context => (
        <button className="button">
          Switch
          <span role="img" aria-label="sun">
            ?
          </span>
          <span role="img" aria-label="moon">
            ?
          </span>
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

export default Button;

Extras Context Provider

În prezent, transmitem o valoare codificată prin furnizor, cu toate acestea, scopul nostru este de a comuta între noapte și zi cu butonul nostru.

Acest lucru necesită mutarea furnizorului nostru într-un fișier separat și introducerea acestuia în propria componentă, în acest caz, numită ThemeContextProvider.

import React, { Component } from "react";
const { Provider, Consumer } = React.createContext();

class ThemeContextProvider extends Component {
  render() {
    return <Provider value={"Day"}>{this.props.children}</Provider>;
  }
}

export { ThemeContextProvider, Consumer as ThemeContextConsumer };

Notă: proprietatea value este acum tratată în noul fișier ThemeContext.js și, prin urmare, ar trebui eliminată din index.js.

Schimbarea contextului

Pentru a conecta butonul, mai întâi adăugăm starea la ThemeContextProvider:

import React, { Component } from "react";
const { Provider, Consumer } = React.createContext();

// Note: You could also use hooks to provide state and convert this into a functional component.
class ThemeContextProvider extends Component {
  state = {
    theme: "Day"
  };
  render() {
    return <Provider value={"Day"}>{this.props.children}</Provider>;
  }
}

export { ThemeContextProvider, Consumer as ThemeContextConsumer };

Apoi, adăugăm o metodă pentru a comuta între zi și noapte:

toggleTheme = () => {
  this.setState(prevState => {
    return {
      theme: prevState.theme === "Day" ? "Night" : "Day"
    };
  });
};

Acum ne schimbăm value proprietate pentru this.state.theme astfel încât să returneze informațiile din stare.

 render() {
    return <Provider value={this.state.theme}>{this.props.children}</Provider>;
  }
}

Apoi, ne schimbăm value la un obiect care conține {theme: this.state.theme, toggleTheme: this.toggleTheme}și actualizați toate locurile în care folosim o singură valoare pentru a căuta theme într-un obiect. Aceasta înseamnă că fiecare theme devine context și fiecare referință la theme pe măsură ce valoarea devine context.theme.

În cele din urmă, îi spunem butonului să asculte onClick eveniment și apoi foc context.toggleTheme – aceasta actualizează consumatorii care utilizează starea de la furnizor. Codul butonului arată astfel:

import React from "react";
import { ThemeContextConsumer } from "./themeContext";

function Button(props) {
  return (
    <ThemeContextConsumer>
      {context => (
        <button onClick={context.toggleTheme} className="button">
          Switch
          <span role="img" aria-label="sun">
            ?
          </span>
          <span role="img" aria-label="moon">
            ?
          </span>
        </button>
      )}
    </ThemeContextConsumer>
  );
}

export default Button;

Butonul nostru acum schimbă imaginea între noapte și zi într-un singur clic!

3evdww

Avertismentele contextuale

Ca toate lucrurile bune din cod, există câteva avertismente în utilizarea contextului:

  • Nu utilizați Context pentru a evita găurirea elementelor de recuzită în jos doar unul sau două straturi. Contextul este excelent pentru gestionarea stării de care sunt necesare porțiuni mari ale unei aplicații. Cu toate acestea, forarea cu propulsoare este mai rapidă dacă transmiteți informațiile pe câteva straturi.

  • Evitați să utilizați Context pentru a salva starea care ar trebui păstrată local. Deci, dacă trebuie să salvați datele de intrare ale unui utilizator, de exemplu, utilizați starea locală și nu contextul.

  • Înfășurați întotdeauna furnizorul în jurul celui mai mic părinte comun posibil din arbore – nu al componentei de cel mai înalt nivel al aplicației. Nu este nevoie de suprasolicitare.

  • În cele din urmă, dacă treceți un obiect ca valoare proprie, monitorizați performanța și refactorizați, după cum este necesar. Probabil că acest lucru nu va fi necesar decât dacă se observă o scădere a performanței.

Învelire

Acest exemplu este destul de simplu și probabil ar fi mai ușor să puneți starea în aplicație și să o transmiteți prin recuzită. Cu toate acestea, sperăm că arată puterea de a avea consumatori care pot accesa date independent de componentele de deasupra lor în arbore.

Pentru a afla mai multe despre React Context și alte caracteristici extraordinare ale React, vă puteți alătura listei de așteptare pentru my viitoare curs React avansat. Sau, dacă sunteți în căutarea unui prieten mai bun pentru începători, puteți să-l verificați curs introductiv gratuit despre React.

Codificare fericita 🙂