Noțiuni introductive cu biblioteca React Javascript

Publicat: 2022-02-16

Dacă petreci ceva timp în lumea WordPress, probabil că l-ai întâlnit pe Zac Gordon. Zac este un profesor entuziast și fermecător care se concentrează pe Javascript în WordPress. A împrumutat expresia „Learn Javascript Deeply” de la Matt Mullenweg și a făcut-o în sloganul său personal.

În ultimii câțiva ani, Zac a produs cursuri video, conferințe online, discuții live și podcasturi care se concentrează pe a vă învăța cum să utilizați Javascript în WordPress.

Sunt încântat să spun că lucrăm cu Zac la o carte, „React Explained”. React este biblioteca pe care echipa WordPress a ales-o pentru noul editor Gutenberg. Nu sunt singuri - atât Drupal, cât și Magento au ales și React.

Zac postează live pe Twitter progresul său în scris la @zgordon. De asemenea, îl puteți auzi pe Zac vorbind despre React pe podcastul OSTraining.

În acest extras din carte, Zac vă oferă o imagine de ansamblu la nivel înalt despre React și câteva concepte cheie.


Ce este React?

Din cutie, React este o bibliotecă pentru construirea de interfețe cu utilizatorul.

Deși React este o bibliotecă JavaScript, interfețele pe care le construiește React sunt agnostice.

Există mai multe biblioteci însoțitoare React pentru a face ca interfețele pe care le construiești să funcționeze în browser, pe server, în aplicații native și chiar în medii 360 și VR. În această carte, ne concentrăm pe lucrul cu ReactDOM, biblioteca care gestionează adăugarea interfețelor pe care le construim cu React la site-urile web și aplicațiile client. ReactDomServer, ReactNative și React360 sunt, de asemenea, biblioteci pe care poate doriți să le explorați pentru utilizarea interfețelor React în alte medii.

Pe lângă furnizarea de funcții de ajutor pentru construirea de interfețe, arhitectura React vă permite să gestionați interacțiunile cu interfețele dvs., indiferent dacă aceasta implică gestionarea evenimentelor, apeluri API, managementul stării, actualizări ale interfeței sau interacțiuni mai complexe.

React nu oferă atât de multe funcții de ajutor ca unele cadre JavaScript. Acesta este în mare parte motivul pentru care numim React o bibliotecă și nu un cadru. Va trebui să scrieți în continuare JavaScript vanilla atunci când lucrați cu React.


Arhitectura componentelor React explicată

O componentă este o bucată de cod independentă, reutilizabilă (creată printr-o funcție sau clasă).

React folosește o arhitectură de componente pentru construirea de interfețe cu utilizatorul și organizarea codului. Fișierul principal pentru o aplicație simplă React poate arăta cam așa.

 // Import React și alte componente
import React din 'react';
import { render } din 'react-dom';
importați antetul din „./Header”;
import MainContent din './MainContent';
importa subsol din „./Footer”;

function App(){
  întoarcere (
    <div className="app">
      <Header />
      <MainContent />
      <Footer />
    </div>
 );
}

ReactDOM.render( <Aplicație />, document.getElementById("rădăcină"));

Putem vedea aici câteva componente în uz. <Header />, <MainContent /> și <Footer /> sunt toate componente. Funcția App() este, de asemenea, o componentă și putem vedea pe ultima linie a acestui exemplu cum putem folosi biblioteca ReactDOM și metoda ReactDOM.render() pentru a gestiona adăugarea UI pe care o construim la o pagină web.

Dacă săpăm în interiorul componentelor <Header />, <MainContent /> și <Footer />, probabil că vom vedea utilizarea mai multor componente, precum și ceea ce arată ca marcaj HTML.

 import React din "react";
import Ad din „../Ad”;
importați sigla din „../assets/logo.svg”;

funcția implicită de export Header() {
întoarcere (
<header className="app-header">
<Anunț />
<img src={logo} className="app-logo" alt="logo" />
<h1 className="app-title">Numele site-ului</h1>
</header>
);
}

În această componentă <Header /> de mai sus putem vedea că introducem încă o componentă numită <Ad />. Majoritatea aplicațiilor React conțin mai multe straturi de imbricare a componentelor, așa cum vedem cu <App />, <Header /> și <Ad />.

De asemenea, vedem utilizarea elementelor HTML în codul nostru React. Acest lucru este posibil datorită unei biblioteci numite JSX, care vă permite să scrieți „Markup HTML” direct în JavaScript. Deoarece folosim React pentru a crea interfețe cu utilizatorul, iar interfețele cu utilizatorul de pe web implică markup HTML, acest lucru are sens să vedem elemente asemănătoare cu HTML în componentele noastre UI. Vom explora JSX în profunzime în această carte.

Dacă ne uităm la un cod de la o aplicație React simplă construită folosind React 360, biblioteca VR a React, componentele reale pe care le numim ar fi diferite, dar arhitectura componentelor este încă prezentă.

 import React din 'react';
import {
  Text,
  Vedere,
  VrButton,
} din 'react-360';

clasa Slideshow extinde React.Component {
  // Cod eliminat pentru concizie
  întoarcere (
    <View style={styles.wrapper}>
      <View style={styles.controls}>
        <VrButton onClick={this.prevPhoto}>
          <Text>{'Previous'}</Text>
        </VrButton>
        <VrButton onClick={this.nextPhoto}>
         <Text>{'Următorul'}</Text>
        </VrButton>
      <Vizualizare>
        <Text style={styles.title}>{current.title}</Text>
      </Vizualizare>
    </Vizualizare>
  );
}

Codul de mai sus creează mai multe straturi de vizualizări 360 cu unele butoane și text suprapus. Deși codul real ar putea să nu aibă sens complet, ar trebui să fie clar că avem mai multe componente imbricate care reprezintă vizualizarea, butoanele și textul.

Acesta este un exemplu bun, deoarece puteți vedea cum aceleași componente sunt reutilizate în moduri diferite, trecându-le parametri diferiți sau ceea ce React numește recuzită. Înțelegerea modului în care datele trec prin componentele React este importantă pentru înțelegerea arhitecturii tipice a componentelor utilizate pentru construirea cu React.


Fluxul de date React explicat

React urmează o convenție de obținere și setare a datelor în cel mai înalt punct necesar într-o ierarhie de componente pentru ca datele să treacă într-o direcție unică în jos printr-o aplicație. Să aruncăm o privire la acest exemplu și să ne imaginăm câteva dintre tipurile de date de care am avea nevoie pentru diverse componente.

 function App() {
  întoarcere(
    <React.Fragment>
      <Header />
      <Conținut />
      <Bară laterală />
      <Footer />
    </React.Fragment>
  );
}

Ceva precum numele site-ului ar putea trebui să fie disponibil atât pentru <Header /> cât și pentru <Footer />. Conținutul principal pentru pagina respectivă ar trebui să fie transmis la <Conținut />. Este posibil ca unele date widget suplimentare să fie necesare pentru a accesa <Sidebar />.

 function App() {
  const siteTitle = getSiteTitle();
  const widgets = getWidgets();
  const mainContent = getPageContent();
  întoarcere(
    <React.Fragment>
      <Header siteTitle={siteTitle} />
      <Conținut mainContent={mainContent} />
      <Widget-uri din bara laterală={widgeturi} />
      <Footer siteTitle={siteTitle} />
    </React.Fragment>
  );
}

Această convenție de a crea nume de atribute și de a le atribui o valoare este modul în care trecem datele într-o componentă.

Acum, <Header /> și <Footer /> au acces la siteTitle, <Content /> are acces la mainContent, iar <Sidebar /> are acces la widget-urile de care are nevoie.

O notă importantă este că acest model de transmitere a datelor într-o componentă trece datele doar la un nivel. Componentele din interiorul <Header /> nu vor avea acces automat la siteTitle.

 funcția Antet (recuzită) {
  întoarcere(
    <header>
      <p>Putem vedea {props.siteTitle} aici.</p>
      <PageHeader siteTitle={props.siteTitle} />
      <PageSubHeader />
    </header>
  );
}

Puteți vedea aici că în interiorul <Header /> putem apela props.siteTitle și avem acces la acea valoare pe care am trecut-o în el. Cu toate acestea, dacă dorim să avem acces la siteTitle din componenta <PageHeader />, ar trebui să transmitem și manual acele informații.

Când o componentă primește o valoare ca prop, nu ar trebui să o modifice. Recuzitele ar trebui să treacă printr-un arbore de componente ca date imuabile. Acest lucru asigură că orice componentă care face referire la o reclamă, face referire la aceeași valoare ca și componentele sale părinte ale copiilor.

Valoarea unei recuzite ar trebui modificată numai în componenta care a stabilit inițial valoarea recuzitei și a început să o transmită în jos prin arborele componente. În exemplul nostru de cod de mai sus, componenta <App /> ar putea schimba valoarea siteTitle, dar componentele <Header /> sau <PageHeader /> nu ar trebui.

Pentru a înțelege fluxul modului în care datele dinamice sunt actualizate într-o aplicație React implică discuții despre stare și despre modul în care gestionatorii de evenimente pot fi transmiși ca elemente de recuzită.


Explicarea stărilor componentelor React

După cum am aflat, datele curg neschimbate prin componente ca elemente de recuzită. Datele sunt setate la cea mai înaltă componentă din arborele necesare pentru ca toate componentele copiilor să primească informațiile necesare ca elemente de recuzită.

În unele cazuri, aceste date sunt primite o singură dată și nu trebuie modificate. În multe cazuri, totuși, acele date trebuie să rămână dinamice și să aibă capacitatea de a se actualiza în orice moment și ca actualizarea să se reflecte în toate componentele copiilor.

Pentru a urmări datele care se modifică în React, avem un obiect de stare React și un set de funcții de ajutor pentru a actualiza valoarea stării.

Iată un exemplu de contor care s-ar actualiza singur. Valoarea contorului este o valoare care este dinamică în cadrul acestei componente și, prin urmare, reprezintă un exemplu bun pentru momentul în care să se bazeze pe stare. Rețineți că pentru a face componente cu stare, trebuie să folosim clase JavaScript mai degrabă decât funcții.

 class Counter extinde Componenta {
  stare= {
    contor: 0
  };

  handleCount = () => {
    this.setState({
      contor: acest.stare.contor + 1
    });
  };

  randa() {
    întoarcere (
      <div>
        <h1>{this.state.counter}</h1>
        <button onClick={this.handleCount}>Numără în sus!!</button>
      </div>
    );
  }
}

Acum este important de remarcat că această stare se limitează doar la această componentă. Valoarea stării din contor nu ar fi disponibilă pentru componentele copil sau părinte.

Deci, într-un exemplu de exemplu mai complex, cum ar fi mai jos, ar trebui să trecem valoarea counter down ca prop în elementul copil.

 class Counter extinde Componenta {
  stare= {
    număr: 0
  };

  handleCount = () => {
    this.setState({
      numărare: acest.stare.număr + 1
    });
  };

  randa() {
    întoarcere (
      <div>
        <PageHeader count={this.state.count} />
        <button onClick={this.handleCount}>Numără în sus!!</button>
      </div>
    );
  }
}

Elementul de contorizare <PageHeader /> este actualizat de fiecare dată când actualizăm starea în componenta <Counter />

 funcția PageHeader(acozite) {
  returnează <h1>{props.count}</h1>; 
}

Lucrul frumos despre această abordare este că, de fiecare dată când starea este actualizată, o nouă valoare va fi transmisă automat în orice componente secundare, cu valoarea unui prop setată la stare.

Acest lucru ne permite să avem un singur punct de adevăr pentru datele dinamice. Sursa adevărului este valoarea în stare, gestionată dintr-o singură componentă. Toate instanțele acestei valori în componentele copii sunt valori imuabile primite ca elemente de recuzită care nu ar trebui modificate în afara acestei componente.

Componentele care apar deasupra acestei componente în ierarhie nu ar avea acces la aceste date, deoarece acestea sunt transmise numai prin elemente de recuzită. Vedem din nou de ce încercăm să setăm și să gestionăm starea din componentele mai înalte în ierarhie, astfel încât datele să fie disponibile pentru tot ce are nevoie.

Există și alte modele de arhitectură, cum ar fi componentele de ordin superior și API-ul de context, care eludează nevoia de a trece manual tone de elemente de recuzită prin aplicația ta. Le vom explora mai în profunzime mai târziu. Deocamdată dorim să ne asigurăm că înțelegem această prezentare generală la nivel înalt a modului în care funcționează lucrurile în general înainte de a începe să luăm comenzi rapide.

Actualizarea stărilor componente în React

Acum, ce se întâmplă când vrem să declanșăm starea să fie actualizată dintr-o componentă copil?

Imaginați-vă, de exemplu, că, în exemplul de mai sus, am vrut să avem o componentă <Button /> mai degrabă decât un buton codificat greu în componenta noastră principală <Counter />? Acest lucru este de fapt destul de comun în aplicațiile complexe.

Soluția la aceasta, în lumea React, este să treceți funcția de gestionare a evenimentelor care actualizează starea cu setState jos ca prop. Apoi poate fi apelat din orice componentă copil, dar acțiunea va avea loc în componenta originală care a stabilit starea și are capacitatea de a o actualiza și ea. Există și alte modele pentru aceasta, dar această abordare este cea mai de bază.

Dacă nu sunteți familiarizat cu trecerea funcțiilor ca parametri, este complet valid JavaScript vanilla.

Odată ce starea este actualizată, aceasta va fi transmisă prin ierarhia componentelor prin elemente de recuzită.

Iată un exemplu despre cum ar arăta.

 class Counter extinde Componenta {
  stare= {
    număr: 0
  };

  handleCount = () => {
    this.setState({
      numărare: acest.stare.număr + 1
    });
  };

  randa() {
    întoarcere (
      <div>
        <PageHeader count={this.state.count} />
        <Button handleCount={this.handleCount} />        
      </div>
    );
  }
}

funcția PageHeader( recuzită ) { 
  întoarcere( 
    <h1>{props.count}</h1>
 ); 
}

Buton de funcție (recuzită) {
  întoarcere(
    <button onClick={props.handleCount}>Numără în sus!!</button>  
  );
}

Aici putem vedea un exemplu simplu despre modul în care React gestionează fluxul de date. Există un singur punct de adevăr pentru date. Acesta există într-un set de stări și actualizat dintr-o singură componentă. Datele sunt transmise într-un flux unic printr-un arbore de componente imbricate prin elemente de recuzită.

Dacă starea trebuie actualizată dintr-o componentă diferită de locul în care a fost setată inițial, atunci un handler de evenimente poate fi transmis componentului copil necesar ca prop. Acest lucru păstrează datele imuabile și curgătoare într-un singur sens, deoarece chiar dacă o componentă copil declanșează o schimbare, acea schimbare are loc mai sus în componenta originală.

Când atribuim valoarea unui prop la ceva din stare, ca mai jos, acea valoare prop se va actualiza automat ori de câte ori starea se schimbă.

 <PageHeader counter={this.state.count} />

Orice altă componentă copil care face referire la acea valoare prop va primi, de asemenea, actualizarea automat. Aceasta este frumusețea fluxului de date în React.

Poate dura ceva timp pentru a vă obișnui, în funcție de modul în care ați abordat probleme ca acestea cu JavaScript în trecut. Cu toate acestea, toate acestea ar trebui să servească drept un bun punct de plecare pentru ca noi să putem săpă mai adânc în explicarea React.