Alle de grundlæggende React.js-koncepter, der sidder fast i denne ene artikel i Medium

En introduktion til at lære React's Why, What og How

Denne artikel er en tilpasning af en interaktiv guide på jsComplete.com/react-intro. JsComplete-versionen har indlejrede kodeeksempler og links til at navigere i indholdet.
Før du begynder, skal du være opmærksom på, at dette er en begyndervenlig vejledning, der dækker de koncepter, jeg klassificerer som grundlæggende for at arbejde med React. Det er ikke en komplet guide til React, men snarere en komplet introduktion.
I slutningen af ​​denne guide viser jeg et par ressourcer på næste niveau til dig. Denne vejledning baner vejen for dig at forstå dem.

React er defineret som et JavaScript-bibliotek til opbygning af brugergrænseflader. Lad os starte med at tale om de to forskellige dele af denne definition.

React er et JavaScript-bibliotek. Det er ikke nøjagtigt en "ramme". Det er ikke en komplet løsning, og du bliver ofte nødt til at bruge flere biblioteker med React for at danne en løsning. React antager ikke noget om de andre dele i nogen løsning.

Rammer tjener et stort formål, især for unge teams og startups. Når du arbejder med en ramme, er der allerede taget mange smarte designbeslutninger for dig, hvilket giver dig en klar sti til at fokusere på at skrive god applikationsniveau-logik. Rammer kommer dog med nogle ulemper. For erfarne udviklere, der arbejder på store codebases, er disse ulemper undertiden en deal breaker.

Rammer er ikke fleksible, selvom nogle hævder at være. En ramme vil normalt, at du skal kode alt på en bestemt måde. Hvis du prøver at afvige fra den måde, ender rammerne normalt med at kæmpe dig om det. Rammer er normalt også store og fulde af funktioner. Hvis du kun har brug for et lille stykke af dem, skal du alligevel medtage det hele. Dette punkt ændrer sig ganske vist i dag, men det er stadig ikke ideelt. Nogle rammer går modulopbygget, hvilket jeg synes er godt, men jeg er en stor fan af den rene Unix-filosofi:

Skriv programmer, der gør en ting og gør det godt. Skriv programmer for at arbejde sammen.
- Doug McIlroy

React følger Unix-filosofien, fordi det er et lille bibliotek, der fokuserer på kun én ting og på at gøre den ting ekstremt godt. Den "ene ting" er den anden del af React's definition: Bygning af brugergrænseflader.

En brugergrænseflade (UI) er alt, hvad vi sætter foran brugerne for at få dem til at interagere med en maskine. UI'er findes overalt, fra de enkle knapper på en mikrobølgeovn til instrumentbrættet i en rumfærgen. Hvis den enhed, vi forsøger at interface, kan forstå JavaScript, kan vi bruge React til at beskrive et UI til det. Da webbrowsere forstår JavaScript, kan vi bruge React til at beskrive web-UI'er.

Jeg kan godt lide at bruge ordet beskriv her, fordi det er det, vi dybest set gør med React. Vi fortæller det bare, hvad vi vil! React bygger derefter det faktiske brugergrænseflade på vores vegne i webbrowser. Uden React eller lignende biblioteker skulle vi manuelt bygge UI'er med oprindelige Web API'er og JavaScript, og det er ikke så let.

Når du hører udsagnet om, at "React is declarative" er det nøjagtigt, hvad det betyder. Vi beskriver UI'er med React og fortæller det, hvad vi vil have (ikke hvordan man gør det). React vil tage sig af “hvordan” og oversætte vores deklarative beskrivelser (som vi skriver på React-sproget) til faktiske UI'er i browseren. React deler denne enkle deklarative magt med HTML selv, men med React må vi være deklarative for HTML UI'er, der repræsenterer dynamiske data, ikke kun statiske data.

Da React blev frigivet, var der en masse brummer om dens ydeevne, fordi det introducerede den smarte idé om en virtuel DOM, der kan bruges til at forene den faktiske DOM (og vi vil tale om det i det næste afsnit).

DOM er "Document Object Model". Det er browsernes programmeringsgrænseflade til HTML (og XML) dokumenter, der behandler dem som træstrukturer. DOM API kan bruges til at ændre en dokumentstruktur, stil og indhold.

Mens Reacts præstation stadig er en af ​​de vigtigste grunde til, at den er ekstremt populær i dag, klassificerer jeg den ikke som den "bedste" ting ved det. Jeg tror, ​​React var en spilskifter, fordi det skabte et fælles sprog mellem udviklere og browsere, der giver udviklere mulighed for deklarativt at beskrive UI'er og administrere handlinger på deres tilstand i stedet for handlinger på deres DOM-elementer. Det er simpelthen sproget i brugergrænsefladets "resultater". I stedet for at komme med trin til at beskrive handlinger på grænseflader, beskriver udviklere bare grænsefladerne i form af en "endelig" tilstand (som en funktion). Når handlinger sker i denne tilstand, sørger React for at opdatere brugergrænsefladerne i DOM baseret på det (og det gør det effektivt, som vi vil se næste).

Hvis nogen bad dig om at give en grund til, at React er værd at lære, er dette resultatbaserede UI-sprog det. Jeg kalder dette sprog "React sprog".

React-sproget

Sig, at vi har en liste over TODO'er som denne:

const todos: [
  {body: 'Lær React Fundamentals', udført: sand},
  {body: 'Byg en TODOs-app', udført: falsk},
  {body: 'Build a Game', gjort: falsk},
];

Denne todos-matrix er starttilstanden for dit brugergrænseflade. Du skal oprette et brugergrænseflade for at vise dem og administrere dem. UI'en har en formular til at tilføje nye TODO'er, en måde for dig at markere en TODO som komplet og en måde at fjerne alle udfyldte TODO'er.

Hver af disse handlinger kræver, at appen udfører en DOM-operation for at oprette, indsætte, opdatere eller slette DOM-noder. Med React skal du ikke bekymre dig om alle disse DOM-operationer. Du behøver ikke bekymre dig om, hvornår de skal ske, eller hvordan du effektivt udfører dem. Du skal bare placere todos-matrixen i "app" -tilstanden og derefter bruge React-sproget til at "kommandere" React for at få vist denne tilstand på en bestemt måde i UI:

TODO-liste
      {todos.map (todo =>     
  • {todo.body}   )}
// Andre formelementer ...

Du skal ikke bekymre dig om syntaksen endnu, men hvis du spekulerer på, hvad der foregår, kortlagede vi blot en række JavaScript-objekter i en række React-elementer. Mere om det snart.

Derefter kan du fokusere på bare at udføre datafunktioner på denne todos-matrix! Du tilføjer, fjerner og opdaterer elementerne i den matrix, og React afspejler de ændringer, du foretager på dette objekt i UI, der er gengivet i browseren.

Denne mentale model om modellering af UI baseret på en endelig tilstand er lettere at forstå og arbejde med, især når visningerne har masser af dataovergange. Overvej for eksempel det synspunkt, der fortæller dig, hvor mange af dine venner er online. Denne visnings “tilstand” vil kun være et enkelt antal af, hvor mange venner der i øjeblikket er online. Det er ligeglad med, at tre venner for et øjeblik siden kom online, hvorefter en af ​​dem koblet fra, og derefter to blev med. Det ved bare, at på dette nuværende tidspunkt er fire venner online.

Reacts træafstemning

Før vi reagerede, da vi havde brug for at arbejde med en browsers API, der er kendt som DOM API, undgik vi at krydse DOM-træet så meget som muligt, og der er en grund til det. Enhver handling på DOM'en udføres i den samme enkelt tråd, der er ansvarlig for alt andet, der sker i browseren, inklusive reaktioner på brugerbegivenheder som at skrive, rulle, ændre størrelse osv.

Enhver dyre operation på DOM betyder en langsom og ujævn oplevelse for brugeren. Det er ekstremt vigtigt, at dine applikationer udfører de absolutte minimumsoperationer og batches dem, hvor det er muligt. React kom med et unikt koncept for at hjælpe os med at gøre nøjagtigt det!

Når vi beder React om at gengive et træ af elementer i browseren, genererer det først en virtuel repræsentation af det træ og holder det rundt i hukommelsen til senere. Derefter fortsætter den med at udføre DOM-operationerne, der får træet til at dukke op i browseren.

Når vi beder React om at opdatere træet af elementer, det tidligere er gengivet, genererer det en ny virtuel repræsentation af det opdaterede træ. React har nu 2 versioner af træet i hukommelsen!

For at gengive det opdaterede træ i browseren kasserer React ikke det, der allerede er gengivet. I stedet vil den sammenligne de 2 virtuelle versioner af træet, det har i hukommelsen, beregne forskellene mellem dem, finde ud af, hvilke undertræer i hovedtræet skal opdateres og kun opdatere disse undertræer i browseren.

Denne proces er det, der kaldes træforsoningsalgoritmen, og det er det, der gør React til en meget effektiv måde at arbejde med en browsers DOM-træ på. Vi ser snart et eksempel på det.

Udover det erklærende resultatbaserede sprog og den effektive træforsoning er her et par af de andre grunde til, at jeg tror, ​​React fik sin enorme popularitet:

  • Det er svært at arbejde med DOM API. React giver udviklere muligheden for at arbejde med en “virtuel” browser, der er venligere end den rigtige browser. Reager fungerer grundlæggende som din agent, der vil kommunikere med DOM på dine vegne.
  • Reaktion får ofte mærket "Just JavaScript". Dette betyder, at det har en meget lille API at lære, og derefter er dine JavaScript-færdigheder det, der gør dig til en bedre React-udvikler. Dette er en fordel i forhold til biblioteker med større API'er. React API er også mest funktioner (og valgfrit klasser, hvis du har brug for dem). Når du hører, at en UI-visning er en funktion af dine data, i React, er det bogstaveligt talt tilfældet.
  • Learning React betaler sig også stort for iOS- og Android-mobilapplikationer. React Native giver dig mulighed for at bruge dine React-færdigheder til at opbygge native mobile applikationer. Du kan endda dele noget logik mellem dine web-, iOS- og Android-applikationer.
  • React-teamet på Facebook tester alle forbedringer og nye funktioner, der introduceres til React lige der på facebook.com, hvilket øger tilliden til biblioteket blandt samfundet. Det er sjældent at se store og alvorlige bugs i React-udgivelser, fordi de først frigives efter grundig produktionstest på Facebook. React driver også andre stærkt anvendte webapplikationer som Netflix, Twitter, Airbnb og mange flere.

Dit første reaktionseksempel

For at se den praktiske fordel ved træforsoningsprocessen og den store forskel, det gør, lad os arbejde gennem et simpelt eksempel fokuseret på netop dette koncept. Lad os generere og opdatere et træ med HTML-elementer to gange, en gang ved hjælp af det oprindelige Web API og derefter bruge React API (og dets forsoningsarbejde). For at holde dette eksempel enkelt skal jeg ikke bruge komponenter eller JSX (JavaScript-udvidelsen, der er populært brugt med React). Jeg vil også udføre opdateringsfunktionen i en JavaScript-interval-timer. Dette er ikke, hvordan vi skriver React-applikationer, men lad os fokusere på et koncept ad gangen.

Start med denne jsFuldfør legepladssession: jsdrops.com/react-dom1.

I denne session gengives et simpelt HTML-element til displayet ved hjælp af 2 metoder:

Metode nr. 1: Brug af Web DOM API direkte

document.getElementById ('mountNode'). innerHTML = `
  
    Hej HTML    `;

Metode nr. 2: Brug af React's API

ReactDOM.render (
  React.createElement (
    'Div',
    nul,
    'Hej reagerer'
  ),
  document.getElementById ( 'mountNode2'),
);

ReactDOM.render-metoden og React.createElement-metoden er de centrale API-metoder i en React-applikation. Faktisk kan en React-webapplikation ikke eksistere uden at bruge begge disse metoder. Lad mig kort forklare dem:

ReactDOM.render

Dette er dybest set indgangspunktet for en React-applikation i browserens DOM. Det har 2 argumenter:

  1. Det første argument er, hvad der skal gengives til browseren. Dette er altid et "React element".
  2. Det andet argument er HVORFOR at gengive det React-element i browseren. Dette skal være en gyldig DOM-knude, der findes i den statisk gengivne HTML. Eksemplet ovenfor bruger et specielt mountNode2-element, der findes i legepladsens visningsområde (den første mountNode bruges til den oprindelige version).

Hvad er et React-element præcist? Det er et VIRTUELT element, der beskriver et DOM-element. Det er det, React.createElement API-metoden vender tilbage.

React.createElement

I stedet for at arbejde med strenge til at repræsentere DOM-elementer (som i det oprindelige DOM-eksempel ovenfor) i React repræsenterer vi DOM-elementer med objekter ved hjælp af opkald til metoden React.createElement. Disse objekter er kendt som React-elementer.

Funktionen React.createElement har mange argumenter:

  1. Det første argument er HTML “-tagget” for DOM-elementet, der skal repræsenteres, hvilket er div i dette eksempel.
  2. Det andet argument er for alle attributter (som id, href, titel osv.), Vi ønsker, at DOM-elementet skal have. Den enkle div, vi bruger, har ingen attributter, så vi brugte null derinde.
  3. Det tredje argument er indholdet af DOM-elementet. Vi har lagt en "Hello React" streng derinde. Det valgfrie tredje argument og alle de valgfri argumenter efter det danner børnelisten for det gengivne element. Et element kan have 0 eller flere børn.
React.createElement kan også bruges til at oprette elementer fra React-komponenter.

Reaktionselementer oprettes i hukommelsen. For faktisk at få et React-element til at dukke op i DOM, bruger vi ReactDOM.render-metoden, som vil gøre mange ting for at finde ud af den mest optimale måde at reflektere tilstanden af ​​et React-element i det faktiske DOM-træ i browseren.

Når du udfører de 2 metoder i denne kodesession, ser du en "Hello HTML" -boks og en "Hello React" -boks:

Rede-reaktionselementer

Vi har to noder: den ene styres direkte med DOM API og den anden styres med React API (som igen bruger DOM API). Den eneste store forskel mellem måderne vi bygger disse to noder i browseren på er, at vi i HTML-versionen brugte en streng til at repræsentere DOM-træet, mens vi i React-versionen brugte rene JavaScript-opkald og repræsenterede DOM-træet med et objekt i stedet for en streng.

Uanset hvor kompliceret HTML-brugergrænsefladen kommer til at blive, repræsenteres ethvert HTML-element ved brug af React med et React-element.

Lad os tilføje flere HTML-elementer til denne enkle brugergrænseflade. Lad os tilføje et tekstfelt for at læse input fra brugeren. For HTML-versionen kan du bare injicere det nye elements tag direkte i skabelonen:

document.getElementById ('mountNode'). innerHTML = `
  
    Hej HTML         `;

For at gøre det samme med React, kan du tilføje flere argumenter efter det tredje argument for React.createElement ovenfor. For at matche det, der hidtil er i det oprindelige DOM-eksempel, kan vi tilføje et fjerde argument, der er et andet React.createElement-opkald, der giver et inputelement:

ReactDOM.render (
  React.createElement (
    "Div",
    nul,
    "Hej reager",
    React.createElement ( "input")
  ),
  document.getElementById ( 'mountNode2'),
);

Lad os også gengive det aktuelle klokkeslæt i begge versioner. Lad os lægge det i et forudgående element (bare for at give det en monospace-font på legepladsen). Du kan bruge ny dato (). ToLocaleTimeString () til at få vist en enkel tidsstreng. Her er hvad du skal gøre for den oprindelige DOM-version:

document.getElementById ('mountNode1'). innerHTML = `
  
    Hej HTML          
 $ {ny dato (). toLocaleTimeString ()} 

   `;

For at gøre det samme i React tilføjer vi et femte argument til div-elementet på øverste niveau. Dette nye femte argument er et andet React.createElement-opkald, denne gang ved hjælp af et forudtag med den nye dato (). ToLocaleTimeString () -streng til indhold:

ReactDOM.render (
  React.createElement (
    'Div',
    nul,
    'Hej reagerer'
    React.createElement ( 'input'),
    React.createElement (
      'Pre',
      nul,
      ny dato (). toLocaleTimeString ()
    )
  ),
  document.getElementById ( 'mountNode2')
);

Begge versioner gengiver stadig nøjagtigt den samme HTML i browseren.

Som du sandsynligvis tænker ved nu, er det meget sværere at bruge React end den enkle og velkendte indfødte måde. Hvad er det, React gør så godt, at det er værd at opgive den velkendte HTML og skulle lære et nyt API for at skrive, hvad der simpelthen kan skrives i HTML?

Svaret handler ikke om gengivelse af den første HTML-visning. Det handler om, hvad vi skal gøre for at opdatere enhver eksisterende visning i DOM.

Opdatering af React-elementer

Lad os udføre en opdateringsoperation på de DOM-træer, vi har hidtil. Lad os blot markere tidsstrengen hvert sekund.

Vi kan nemt gentage et JavaScript-opkald i en browser ved hjælp af setInterval Web timer API. Lad os placere alle vores DOM-manipulationer for begge versioner i en funktion, navngive den, og brug dem i et setInterval-opkald for at få det til at gentage hvert sekund.

Her er den fulde endelige kode til dette eksempel:

// jsdrops.com/react-dom2
const render = () => {
  document.getElementById ('mountNode'). innerHTML = `
    
      Hej HTML              
 $ {ny dato (). toLocaleTimeString ()} 
       `;
  ReactDOM.render (
    React.createElement (
      'Div',
      nul,
      'Hej reagerer'
      React.createElement ('input', null),
      React.createElement (
        'Pre',
        nul,
        ny dato (). toLocaleTimeString ()
      )
    ),
    document.getElementById ( 'mountNode2')
  );
};
setInterval (gengivelse, 1000);

Tjek resultatet af eksekvering af denne kode på jsdrops.com/react-dom2, og bemærk, hvordan tidsstrengen markerer hvert sekund i begge versioner. Vi opdaterer nu vores brugergrænseflade i DOM.

Dette er det øjeblik, hvor React potentielt vil sprænge dit sind. Hvis du prøver at indtaste noget i tekstfeltet i den oprindelige DOM-version, er du ikke i stand til det. Dette forventes meget, fordi vi dybest set smider hele DOM-knuden på hvert kryds og forny den. Hvis du prøver at skrive noget i tekstfeltet, der er gengivet med React, kan du dog gøre det!

Selvom hele React-gengivelseskoden er inden for den tickende timer, ændrer React kun indholdet af forelementet og ikke hele DOM-træet. Dette er grunden til, at tekstindtastningsfeltet ikke blev regenereret, og vi kunne skrive det.

Du kan se de forskellige måder, vi opdaterer DOM visuelt, hvis du inspicerer de to DOM-noder i et Chrome DevTools-elementpanel. Panelet Chrome DevTools-elementer fremhæver alle DOM-elementer, der bliver opdateret. Du vil se, hvordan den oprindelige HTML-version regenererer hele div # mountNode-beholderen med hvert kryds, mens React smart kun regenererer fortagget i dets div # mountNode2-container.

Dette er Reacts smarte, forskellige adskillelsesalgoritme i handling. Det opdateres kun i det vigtigste DOM-træ, hvad der faktisk skal opdateres, mens det holder alt andet det samme. Denne adskilte proces er mulig på grund af React's virtuelle DOM-repræsentation, som den holder rundt i hukommelsen. Uanset hvor mange gange UI-visningerne skal regenereres, vil React kun tage de nødvendige "delvise" opdateringer til browseren.

Ikke kun er denne metode meget mere effektiv, men den fjerner også et stort lag med kompleksitet på den måde, vi tænker på at opdatere UI'er. Når vi har reageret, gør alle beregningerne om, hvorvidt vi skal eller ikke skal opdatere DOM, gør det muligt for os at fokusere på at tænke på vores data (tilstand) og måden at beskrive en UI til. Vi administrerer derefter opdateringerne på datatilstanden efter behov uden at bekymre os om de trin, der er nødvendige for at afspejle disse opdateringer i selve brugergrænsefladen i browseren (fordi vi ved, at React vil gøre netop det, og det vil gøre det på en effektiv måde!)

React handler om komponenter

I React beskriver vi UI'er ved hjælp af komponenter, der er genanvendelige, komposible og tilstødelige.

Vi definerer små komponenter og sætter dem derefter sammen for at danne større. Alle komponenter små eller store kan genanvendes, også på tværs af forskellige projekter.

Du kan tænke på komponenter som enkle funktioner (i ethvert programmeringssprog). Vi kalder funktioner med nogle input, og de giver os nogle output. Vi kan genbruge funktioner efter behov og komponere større funktioner fra mindre.

Reaktionskomponenter er nøjagtig de samme; deres input er et sæt "rekvisitter", og deres output er en beskrivelse af et brugergrænseflade. Vi kan genbruge en enkelt komponent i flere UI'er, og komponenter kan indeholde andre komponenter. Den grundlæggende form for en React-komponent er faktisk en almindelig gammel JavaScript-funktion.

Nogle React-komponenter er rene, men du kan også introducere bivirkninger i en komponent. For eksempel kan en komponent muligvis ændre HTML “titlen” på en webside, når den monteres i browseren, eller den kan rulle browservisningen til en bestemt position.

Det vigtigste er, at en React-komponent kan have en privat tilstand til at indeholde data, der kan ændres i løbet af komponentens livscyklus. Denne private stat er en implicit del af det input, der driver komponentens output, og det er faktisk det, der giver React dens navn!

Hvorfor kaldes React alligevel "React"?
Når tilstanden af ​​en React-komponent (som er en del af dens input) ændres, ændres UI'en, den repræsenterer (dens output), også. Denne ændring i beskrivelsen af ​​brugergrænsefladen skal afspejles i den enhed, vi arbejder med. I en browser skal vi opdatere DOM-træet. I en React-applikation gør vi det ikke manuelt. React vil blot reagere på tilstandsændringerne og automatisk (og effektivt) opdatere DOM, når det er nødvendigt.

Oprettelse af komponenter ved hjælp af funktioner

En React-komponent - i sin enkleste form - er en almindelig gammel JavaScript-funktion:

// jsdrops.com/bx1
funktionsknap (rekvisitter) {
  // Returnerer et DOM / React-element her. For eksempel:
  return  {props.label} ;
}
// At gengive et knapelement i browseren
ReactDOM.render (

Bemærk, hvordan jeg skrev, hvad der ligner HTML i den returnerede output af knappen-funktionen ovenfor. Dette er hverken HTML eller JavaScript, og det reagerer ikke engang. Dette er JSX. Det er en udvidelse til JavaScript, der giver os mulighed for at skrive funktionskald i en HTML-lignende syntaks.

Gå videre og prøv at returnere ethvert andet HTML-element i knapfunktionen, og se, hvordan de alle understøttes (for eksempel returnere et inputelement eller et textarea-element).

JSX er ikke HTML

JSX forstås ikke af browsere. Hvis du prøver at udføre knapfunktionen i en almindelig browserkonsol, klager den over det første tegn i JSX-delen:

Det, browsere forstår (i betragtning af at React-biblioteket er inkluderet) er React.createElementAPI-opkaldene. Det samme knapeksempel kan skrives uden JSX som følger:

// jsdrops.com/bx2
funktionsknap (rekvisitter) {
  return React.createElement (
    "knap",
    {type: "indsende"},
    props.label
  );
}
ReactDOM.render (
  React.createElement (knap, {label: "Gem"}),
  mountNode
);

Du kan bruge React på denne måde (uden JSX). Du kan udføre knapfunktionen i en browser direkte (efter indlæsning af React-biblioteket), og tingene fungerer fint. Vi kan dog gerne se og arbejde med HTML i stedet for at håndtere funktionsopkald. Hvornår var sidste gang du byggede et websted med bare JavaScript og ikke brugte HTML? Det kan du, hvis du vil, men ingen gør det. Derfor findes JSX.

JSX er dybest set et kompromis. I stedet for at skrive React-komponenter ved hjælp af React.createElement-syntaks, bruger vi en syntaks, der ligner HTML og bruger derefter en compiler til at oversætte den til React.createElement-opkald.

En kompilator, der oversætter en form for syntaks til en anden, kaldes en "transpiler". For at oversætte JSX kan vi bruge transpilere som Babel eller TypeScript. For eksempel bruger jsComplete-legepladsen TypeScript til at transponere enhver JSX, du lægger i den. Når du bruger create-react-app, bruger den genererede app internt Babel til at transponere din JSX.

Du kan bruge babeljs.io/repl/ for at se, hvilken JSX-syntaks der konverteres til til React, men JSX kan også bruges alene. Det er ikke en ting, der kun reagerer.

Så en React-komponent er en JavaScript-funktion, der returnerer et React-element (normalt med JSX). Når JSX bruges, bliver

Navnet skal starte med en stor bogstav

Bemærk, hvordan jeg navngav komponenten "Knap". Det første bogstav, der er et stort bogstav, er faktisk et krav, da vi har at gøre med en blanding af HTML-elementer og React-elementer. En JSX-kompilator (som Babel) vil betragte alle navne, der starter med et lille bogstav, som navne på HTML-elementer. Dette er vigtigt, fordi HTML-elementer sendes som strenge til React.createElement-opkald, mens React-elementer skal sendes som variabler:

Gå videre og prøv at navngive React-komponenten "knap" i stedet for "Knap" og se, hvordan ReactDOM fuldstændigt ignorerer funktionen og gengiver et almindeligt tomt HTML-knapelement.

// jsdrops.com/bx3
// Forkert:
funktionsknap () {
  return 
Min fancy knap
; };
// Følgende gengiver en HTML-knap
// (og ignorér den fancy knap-funktion)
ReactDOM.render (, mountNode);

Det første argument er et objekt med "rekvisitter"

Ligesom HTML-elementer kan tildeles attributter som id eller titel, kan et React-element også modtage en liste over attributter, når det bliver gengivet. Knapelementet ovenfor (jsdrops.com/bx2) modtog en labelattribut. I React er listen over attributter modtaget af et React-element kendt som rekvisitter. En React-funktionskomponent modtager denne liste som dets første argument. Listen sendes som et objekt med taster, der repræsenterer attributternavne og værdier, der repræsenterer de værdier, der er tildelt dem.

Når du bruger en funktionskomponent, behøver du ikke at navngive det objekt, der indeholder listen over attributter, som "rekvisitter", men det er standardpraksisen. Når du bruger klassekomponenter, som vi vil gøre nedenfor, præsenteres den samme liste med attributter altid med en speciel instansegenskab, der hedder rekvisitter.

Bemærk, at modtagelse af rekvisitter er valgfri. Nogle komponenter har ikke nogen rekvisitter. En komponents returneringsværdi er dog ikke valgfri. En React-komponent kan ikke returnere “udefineret” (hverken eksplicit eller implicit). Det skal returnere en værdi. Det kan returnere “null” for at få rendereren til at ignorere dens output.

Jeg kan godt lide at bruge objektdestrukturering, hver gang jeg bruger komponentrekvisitter (eller angiv egentlig,). F.eks. Kan knapkomponentfunktionen skrives på denne måde med rekvisitter, der ødelægger:

const-knap = ({label}) => (
   {label} 
);

Denne tilgang har mange fordele, men den vigtigste er at visuelt inspicere, hvilke rekvisitter, der bruges i en komponent, og sørge for, at en komponent ikke modtager ekstra rekvisitter, som ikke er nødvendige.

Bemærk, hvordan jeg brugte en pilefunktion i stedet for en regelmæssig. Dette er bare en stilindstilling for mig personligt. Nogle mennesker foretrækker den almindelige funktionstil, og der er ikke noget galt med det. Jeg tror, ​​det, der er vigtigt, er at være i overensstemmelse med den stil, du vælger. Jeg vil bruge pilefunktioner her, men fortolker det ikke som et krav.

Udtryk i JSX

Du kan inkludere et JavaScript-udtryk ved hjælp af et par krøllede parenteser hvor som helst inden for JSX:

// jsdrops.com/bx4
const RandomValue = () => (
  
    {Math.floor (Math.random () * 100)}    );
ReactDOM.render (, mountNode);

Bemærk, at kun udtryk kan inkluderes i disse krøllede parenteser. For eksempel kan du ikke inkludere en regelmæssig if-statement, men et ternært udtryk er okay. Alt, der returnerer en værdi, er okay. Du kan altid placere en hvilken som helst kode i en funktion, få den til at returnere noget og kalde den funktion inden i de krøllede parenteser. Hold dog den logik, du lægger i disse krøllede parenteser til et minimum.

JavaScript-variabler er også udtryk, så når komponenten modtager en liste med rekvisitter, kan du bruge disse rekvisitter inde i krøllede parenteser. Sådan brugte vi {label} i knapeksemplet.

JavaScriptobjektlitterære er også udtryk. Nogle gange bruger vi et JavaScript-objekt inde i krøllede parenteser, der får det til at ligne dobbelt krøllede parenteser: {{a: 42}}. Dette er ikke en anden syntaks; det er bare en bogstavelig genstand defineret inde i de almindelige JSX krøllede konsoller.

For eksempel er en brugssag til brug af et objekt bogstaveligt i disse krøllede parenteser at videregive et CSS-stilobjekt til den specielle stilattribut i React:

// jsdrops.com/bx5
const ErrorDisplay = ({meddelelse}) => (
  
    {besked}    );
ReactDOM.render (
  ,
  mountNode
);

Stilattributten ovenfor er en speciel. Vi bruger et objekt som dets værdi, og det objekt definerer typografierne, som om vi indstiller dem gennem JavaScript DOM API (navn på kamel-sag-egenskaber, strengværdier). React oversætter disse stilobjekter til inline CSS-stilattributter. Dette er ikke den bedste måde at style en React-komponent på, men jeg synes det er ekstremt praktisk at bruge, når jeg anvender betingede stilarter på elementer. For eksempel er her en komponent, der vil udsende sin tekst i enten grøn eller rød tilfældigt omkring halve tiden:

// jsdrops.com/bx6
klasse ConditionalStyle udvider React.Component {
  render () {
    Vend tilbage (
      
        Hvordan kan du lide dette?            );   } }
ReactDOM.render (
  ,
  mountNode,
);

Logikken for denne styling er lige der i komponenten. Det kan jeg lide! Dette er lettere at arbejde med end betinget med at bruge et klassens navn og derefter gå på sporet for, hvad det klassens navn laver i det globale CSS-stilark.

JSX er ikke et skabelonsprog

Nogle biblioteker, der beskæftiger sig med HTML, indeholder et skabelonsprog til det. Du skriver dine dynamiske visninger med en "forbedret" HTML-syntaks, der har sløjfer og betingelser. Disse biblioteker bruger derefter JavaScript til at konvertere skabeloner til DOM-operationer. DOM-operationerne kan derefter bruges i browseren til at vise DOM-træet beskrevet af den forbedrede HTML.

Reaktion eliminerede dette trin. Vi sender overhovedet ikke en skabelon til browseren med en React-applikation. Vi sendte det et træ objekter beskrevet med React API. React bruger disse objekter til at generere de DOM-operationer, der er nødvendige for at få vist det ønskede DOM-træ.

Når en HTML-skabelon bruges, analyserer biblioteket din applikation som en streng. En React-applikation er parset som et træ af objekter.

Mens JSX måske ser ud som et skabelonsprog, er det virkelig ikke. Det er blot en JavaScript-udvidelse, der giver os mulighed for at repræsentere React's træstræ med en syntaks, der ligner en HTML-skabelon. Browsere behøver ikke at beskæftige sig med JSX overhovedet, og React behøver heller ikke at tackle det! Det er kun kompilatoren, der gør. Hvad vi sender til browseren er skabelonfri og JSX-fri kode.

For eksempel, for den todos-matrix, vi har set ovenfor, hvis vi skal vise denne matrix i en brugergrænseflade ved hjælp af et skabelonsprog, bliver vi nødt til at gøre noget som:

      <% FOR hver todo på listen over todos%>     
  • <% = todo.body%>
  •   <% END FOR%>
<%%> Er en syntaks, der repræsenterer de dynamiske forbedrede dele. Du kan muligvis også se {{}} syntaks. Nogle skabelonsprog bruger specielle attributter til deres forbedrede logik. Nogle skabelonsprog bruger whitespace-indrykning (off-side-regel).

Når der sker ændringer i todos-matrixen (og vi er nødt til at opdatere, hvad der er gengivet i DOM med et skabelonsprog), bliver vi enten nødt til at gengive den skabelon igen eller beregne, hvor vi i DOM-træet skal afspejle ændringen i todos-matrixen .

I en React-applikation er der overhovedet intet skabelonsprog. I stedet bruger vi JSX:

      {todos.map (todo =>     
  • {todo.body}   )}

Hvilket, før det bruges i browseren, oversættes til:

React.createElement (
  "Ul",
  nul,
  todos.map (todo =>
    React.createElement ("li", null, todo.body)
  ),
);

React tager dette træ af objekter og omdanner det til et træ af DOM-elementer. Fra vores synspunkt er vi færdige med dette træ. Vi administrerer ikke handlinger på det. Vi administrerer bare handlinger i selve todos-matrixen.

Oprettelse af komponenter ved hjælp af klasser

React understøtter også oprettelse af komponenter gennem JavaScript-klassen syntaks. Her er det samme knapkomponenteksempel skrevet med klassesyntaxen:

// jsdrops.com/bx7
klasse-knap udvider React.Component {
  render () {
    Vend tilbage (
       {this.props.label} 
    );
  }
}
// Brug det (samme syntaks)
ReactDOM.render (

I denne syntaks definerer du en klasse, der udvider React.Component, som er en af ​​hovedklasserne i React på topniveau API. En klassebaseret React-komponent skal mindst definere en forekomstmetode med navnet render. Denne gengivelsesmetode returnerer det element, der repræsenterer output fra et objekt, der er instantieret fra komponenten. Hver gang vi bruger den klassebaserede komponentkomponent (ved at gengive en ), reagerer React et objekt fra denne klassebaserede komponent og bruger objektets repræsentation til at oprette et DOM-element. Det vil også knytte det DOM-gengivne element til det forekomst, det oprettede fra klassen.

Bemærk, hvordan vi brugte this.props.label inde i den gengivne JSX. Hver komponent får en speciel forekomstsejendom, der kaldes rekvisitter, der indeholder alle værdier, der er overført til denne komponentelement, da det blev instantieret. I modsætning til funktionskomponenter modtager renderfunktionen i klassebaserede komponenter ingen argumenter.

Funktioner vs klasser

Komponenter oprettet med funktioner, der plejede at være begrænset i React. Den eneste måde at gøre en komponent "stateful" var at bruge klassesyntaxen. Dette har ændret sig med frigivelsen af ​​“React Hooks”, der begynder med React version 16.8, som blev frigivet i begyndelsen af ​​2019. React hooks-frigivelsen introducerede en ny API for at gøre en funktionskomponent tilstand (og give den mange andre funktioner).

Med denne nye API kan det meste af det, der normalt gøres med React, udføres med funktioner. Den klassebaserede syntaks er kun nødvendig i avancerede og meget sjældne tilfælde.

Jeg tror, ​​at den nye API langsomt vil erstatte den gamle, men det er ikke den eneste grund til, at jeg vil opfordre dig til at bruge den (udelukkende hvis du kan).

Jeg har brugt begge API'er i store applikationer, og jeg kan fortælle dig, at den nye API er langt mere overlegen end den gamle af mange grunde, men her er dem, jeg personligt synes er de vigtigste:

  • Du behøver ikke at arbejde med klasse "instanser" og deres implicitte tilstand. Du arbejder med enkle funktioner, der opdateres på hver gengivelse. Staten erklæres eksplicit, og intet er skjult. Alt dette betyder dybest set, at du vil støde på mindre overraskelser i din kode.
  • Du kan gruppere relateret tilstødende logik og opdele den i selvforsynende komponerbare og delbare enheder. Dette gør det lettere at nedbryde komplekse komponenter i mindre dele. Det gør også testning af komponenter lettere.
  • Du kan forbruge enhver statlig logik på en erklærende måde og uden at skulle bruge nogen hierarkisk "indlejring" i komponenter træer.

Mens klassebaserede komponenter fortsat vil være en del af React i en overskuelig fremtid, som en nykommer i økosystemet, tror jeg, det er fornuftigt for dig at starte med bare funktioner (og kroge) og fokusere på at lære det nye API (medmindre du skal arbejde med en kodebase, der allerede bruger klasser).

Komponenter vs elementer
Du finder måske ordene "komponent" og "element" blandet i React-guider og tutorials derude. Jeg tror, ​​at en React-elev har brug for at forstå de vigtige sondringer.
En reaktionskomponent er en skabelon. En plan. En global definition. Dette kan enten være en funktion eller en klasse (med en gengivelsesmetode).
Et React Element er det, der vender tilbage fra komponenter. Det er et objekt, der praktisk talt beskriver de DOM-knudepunkter, som en komponent repræsenterer. Med en funktionskomponent er dette element det objekt, som funktionen returnerer, og med en klassekomponent er elementet det objekt, som komponentens render-metode returnerer. Reaktionselementer er ikke det, du ser i browseren. De er bare objekter i hukommelsen, og du kan ikke ændre noget ved dem.
Reager internt opretter, opdaterer og ødelægger objekter for at finde ud af det DOM-elementstræ, der skal gengives til browseren. Når du arbejder med klassekomponenter, er det almindeligt at henvise til deres browser-gengivne DOM-elementer som komponentforekomster. Du kan gengive mange tilfælde af den samme komponent. Forekomsten er det "dette" nøgleord, som du bruger i klassebaserede komponenter. Du behøver ikke at oprette en instans manuelt fra en klasse. Du skal bare huske, at den er der et sted i React's hukommelse. For funktionskomponenter bruger React bare tilkaldelsen af ​​funktionen til at bestemme, hvilket DOM-element der skal gengives.

Fordele ved komponenter

Udtrykket “komponent” bruges af mange andre rammer og biblioteker. Vi kan endda skrive webkomponenter indfødt ved hjælp af HTML5-funktioner som brugerdefinerede elementer og HTML-import.

Komponenter, hvad enten vi arbejder med dem indfødt eller gennem et bibliotek som React, har mange fordele.

For det første gør komponenter din kode mere læsbar og lettere at arbejde med. Overvej denne brugergrænseflade:


 

Hvad repræsenterer denne brugergrænseflade? Hvis du taler HTML, kan du analysere det hurtigt her og sige, "det er et klik, der kan klikkes." Hvis vi skal konvertere denne brugergrænseflade til en komponent, kan vi bare navngive det ClickableImage!

Når tingene bliver mere komplekse, bliver denne parsning af HTML sværere, så komponenter giver os mulighed for hurtigt at forstå, hvad en UI repræsenterer ved hjælp af det sprog, vi er tilpas med. Her er et større eksempel:


  
  
  

Uden at se på den aktuelle HTML-kode ved vi nøjagtigt, hvad dette brugergrænseflade repræsenterer. Hvis vi endvidere skal ændre output fra det resterende tegnafsnit, ved vi nøjagtigt, hvor vi skal hen.

React-komponenter kan også genbruges i den samme applikation og på tværs af flere applikationer. Her er f.eks. En mulig implementering af ClickableImage-komponenten:

const ClickableImage = ({href, src}) => {
  Vend tilbage (
    
      
    
  );
};

At have variabler til både href- og src-rekvisitterne er det, der gør denne komponent genanvendelig. For at bruge denne komponent kan vi for eksempel gengive den med et sæt rekvisitter:

Og vi kan genbruge det ved at bruge et andet sæt rekvisitter:

I funktionel programmering har vi begrebet rene funktioner. Disse er dybest set beskyttet mod enhver udenrigsstat; hvis vi giver dem samme input, får vi altid den samme output.
Hvis en React-komponent ikke afhænger af (eller ændrer) noget uden for dens definition (for eksempel hvis den ikke bruger en global variabel), kan vi også mærke den komponent ren. Rene komponenter har en bedre chance for at blive genanvendt uden problemer.

Vi opretter komponenter, der repræsenterer visninger. For ReactDOM repræsenterer React-komponenterne, vi definerer, HTML DOM-noder. ClickableImage-komponenten ovenfor var sammensat af to indlejrede HTML-elementer.

Vi kan tænke på HTML-elementer som indbyggede komponenter i browseren. Vi kan også bruge vores egne brugerdefinerede komponenter til at komponere større. Lad os f.eks. Skrive en komponent, der viser en liste over søgemaskiner.

const SearchEngines = () => {
  Vend tilbage (
    
                     ); };

Bemærk, hvordan jeg brugte komponenten ClickableImage til at komponere SearchEngines-komponenten!

Vi kan også gøre SearchEngines-komponenten genanvendelig ved at udtrække dens data til en variabel og designe dem til at arbejde med denne variabel.

For eksempel kan vi introducere en datarray i et format som:

const data = [
  {href: "http://google.com", src: "google.png"},
  {href: "http://bing.com", src: "bing.png"},
  {href: "http://yahoo.com", src: "yahoo.png"}
];

Derefter, for at få til at fungere, kortlægger vi blot datarrayet fra en liste med objekter i en liste over ClickableImage-komponenter:

const SearchEngines = ({motorer}) => {
  Vend tilbage (
    
      {motors.map (engine => )}
    
  );
};
ReactDOM.render (
 ,
 document.getElementById ( "mountNode")
);

Denne søgemaskine kan arbejde med enhver liste over søgemaskiner, vi giver den.

Hvad er kroge nøjagtigt?

En krog i en React-komponent er et opkald til en speciel funktion. Alle krogfunktioner begynder med ordet “brug”. Nogle af dem kan bruges til at give en funktionskomponent med stateful elementer (som useState), andre kan bruges til styrede bivirkninger (som useEffect) eller til cache / memoize funktioner og objekter (som useCallback). Kroge er meget magtfulde og himlen er grænsen, når det kommer til ting, du kan gøre med dem.

React hook-funktioner kan kun bruges funktionskomponenter. Du kan ikke bruge dem i klassekomponenter.

For at se et eksempel på den grundlæggende useState-krog, lad os få knappen til at svare på en klikhændelse. Lad os opretholde antallet af gange, det klikkes på i en "tælle" -variabel, og vis værdien af ​​den variabel som etiketten på den knap, den gengiver.

const-knap = () => {
  lad tælle = 0;
  Vend tilbage (
     {count} 
  );
};
ReactDOM.render (

Denne tællervariabel vil være det tilstandselement, som vi har brug for at introducere til eksemplet. Det er et stykke data, som brugergrænsefladen afhænger af (fordi vi viser dem), og det er et tilstandselement, fordi det vil ændre sig over tid.

Hver gang du definerer en variabel i din kode introducerer du en tilstand, og hver gang du ændrer værdien på den variabel, muterer du den tilstand. Husk det.

Inden vi kan ændre værdien af ​​tælletilstanden, er vi nødt til at lære om begivenheder.

Svar på brugerbegivenheder

Du kan tilføje en begivenhedshåndterer med en "onEvent" egenskab (til knapelementet i dette tilfælde). Dette kan være en onClick, onMouseOver, onScroll, onSubmit osv.

Det, vi har brug for her, er en onClick-begivenhed, og vi definerer den bare som en attribut på målelementet. For eksempel at gøre programloggen til en meddelelse til konsollen, hver gang der klikkes på knappen, kan vi gøre noget som:

const-knap = () => {
  lad tælle = 0;
  Vend tilbage (
     console.log ('Klik på knappen')}>
      {tælle}
    
  );
};
ReactDOM.render (

I modsætning til DOM-versionen af ​​onClick-attributen (som bruger en streng) bruger React's onClick-attribut en funktionsreference. Du angiver det inden i krøllede parenteser.

funktion func () {}
<-knap onClick = {func} />

Bemærk, hvordan vi har bestået func-referencen (navnet) som onClick-behandleren. Vi påkaldte func ikke derinde. React aktiverer func, når der klikkes på knappen.

For onClick-begivenheden i knappen-komponenten ovenfor "inlineerede" vi en funktionsdefinition, der, når der kaldes, vil sende en meddelelse til konsollen. Hver gang vi klikker på knappen, aktiveres onClick-behandleren (inline pil-funktionen), og vi får vist den meddelelse.

Bemærk, hvordan begivenhedsnavnet er kamel-sag. Alle DOM-relaterede attributter (som håndteres af React) skal være camel-case (og React viser en fejl, hvis det ikke er tilfældet). React understøtter også brug af brugerdefinerede HTML-attributter, og disse skal være i format med små bogstaver.
Nogle DOM-attributter i React er lidt anderledes end hvad de gør i den almindelige DOM API. Et eksempel på det er onChange-begivenheden. I en almindelig browser fyres det normalt, når du klikker uden for et formularfelt (eller tager ud af det). I React tændes onChange, hver gang værdien af ​​et formularfelt ændres (på hvert tegn tilføjet / fjernet).
Nogle attributter i React benævnes forskelligt fra deres HTML-ækvivalent. Et eksempel på dette er attributten className i React, hvilket svarer til brugen af ​​klasseattributten i HTML. For en komplet liste over forskellene mellem React attribut og DOM attributter, se jscomplete.com/react-attribut.

Læsning og opdatering af tilstand

For at spore tilstandsopdateringer og udløse virtuel DOM-afvigelse og reel DOM-afstemning, skal React være opmærksom på alle ændringer, der sker med alle tilstandselementer, der bruges i komponenter. For at gøre dette på en effektiv måde kræver React brug af specielle getters og seters for hvert tilstandselement, du introducerer i en komponent. Det er her useState-krogen kommer i spil. Det definerer et statligt element og giver os tilbage en getter og setter for det!

Her er hvad vi har brug for det optællingsstatuselement, vi forsøger at implementere:

const [count, setCount] = React.useState (0);

UseState-funktionen returnerer en matrix med nøjagtigt 2 emner. Det første element er en værdi (getter), og det andet element er en funktion (setter). Jeg brugte array-destruktion for at give disse emner navne. Du kan give dem alle de navne, du ønsker, men [name, setName] er konventionen.

Det første element "værdi" kan være en streng, nummer, array eller andre typer. I dette tilfælde havde vi brug for et tal, og vi var nødt til at initialisere dette nummer med 0. Argumentet til React.useStateis blev brugt som den oprindelige værdi af tilstandselementet.

Det andet punkt “funktion” ændrer værdien af ​​tilstandselementet, når det påberåbes (og det udløser DOM-behandling om nødvendigt). Hver gang setCount-funktionen aktiveres, vil React gendanne knappen-komponenten, der opdaterer alle variabler, der er defineret i komponenten (inklusive tællerværdien). Argumentet, vi overfører til setCount, bliver den nye værdi for tælling.

Hvad vi skal gøre for at få knappen til at øge dens etiket er at påkalde funktionen setCount inden for onClick-begivenheden og videregive den aktuelle tællingsværdi, der er øget med 1, til den. Her er den fulde kode for eksemplet på knappen med etiketforøgelse:

const-knap = () => {
  const [count, setCount] = useState (0);
  Vend tilbage (
     setCount (count + 1)}>
      {tælle}
    
  );
};
ReactDOM.render (

Gå videre og test det. Knappen øger nu sin etiket ved hvert klik.

Bemærk, hvordan vi ikke implementerede nogen handlinger for at ændre selve brugergrænsefladen. I stedet implementerede vi en handling for at ændre et JavaScript-objekt (i hukommelsen)! Vores UI-implementering fortællede dybest set React, at vi ønsker, at knappen på knappen altid skal afspejle countobjectets værdi. Vores kode foretog ingen DOM-opdateringer. Reaktion gjorde.

Bemærk også, hvordan jeg brugte const-nøgleordet til at definere tælling, selvom det er en værdi, der bliver ændret! Vores kode ændrer ikke denne værdi. Reagerer, når den bruger et nyt opkald af knappen-funktionen til at gengive brugergrænsefladen til dens nye tilstand. I det friske opkald giver useState-funktionskaldet os en ny frisk tællingsværdi.

UseState-funktionen er tilgængelig globalt på legepladsen. Dette er bare et alias til React.useState. I din kode kan du bruge navngivne importer for at have useState tilgængeligt direkte inden for et modul:
import React, {useState} fra 'react';

Du har brug for et par flere eksempler for at værdsætte denne kraft. Lad os tilføje nogle flere funktioner til dette grundlæggende eksempel. Lad os have mange knapper, og få dem alle til at øge en enkelt værdi for delt tælling.

Arbejde med flere komponenter

Lad os opdele knappen-komponenten, vi har hidtil, i to komponenter:

  • Opbevar en knapkomponent for at repræsentere et knapelement, men med en statisk etiket.
  • Tilføj en ny skærmkomponent for at få vist tællerens værdi.

Den nye skærmkomponent vil være en rent præsentativ komponent uden nogen tilstand eller interaktioner af sig selv. Det er normalt. Ikke hver React-komponent skal have tilstrækkelige kroge eller være interaktiv.

const Display = (rekvisitter) => (
  
 Tæller værdi her ... 
);

Displaykomponentens ansvar er simpelthen at vise en værdi, den får som en rekvisitter. For eksempel er det faktum, at et pre element blev brugt til at være vært for værdien, en del af dette ansvar. Andre komponenter i dette program siger ikke noget om det!

Gengivende søskendele

Vi har nu to elementer, der skal gengives: Knap og skærm. Vi kan ikke gengive dem direkte ved siden af ​​hinanden sådan:

// Dette fungerer ikke
ReactDOM.render (

Tilstødende elementer kan ikke gengives på denne måde i React, fordi hver af dem bliver oversat til et funktionsopkald, når JSX konverteres. Du har et par muligheder for at tackle dette problem.

Først kan du videregive en række elementer til ReactDOM.render og indsætte i den array så mange React-elementer, som du ønsker.

Mulighed 1

ReactDOM.render ([

Dette er normalt en god løsning, når alle de elementer, du gengiver, kommer fra en dynamisk kilde. Det er dog ikke ideelt til det tilfælde, vi laver her.

En anden mulighed er at gøre søskende React-elementer til børn af et andet React-element. For eksempel kan vi bare omslutte dem i et div-element.

Mulighed nr. 2

ReactDOM.render (
  
            ,   mountNode );

React API understøtter denne reden. Faktisk har React et specielt objekt, hvis du har brug for at vedlægge flere tilstødende elementer som dette uden at introducere en ny DOM-overordnernode. Du kan bruge React.Fragment:

Mulighed # 3

ReactDOM.render (
  
    
    
  ,
  mountNode
);

Denne sag er så almindelig i React, at JSX-udvidelsen har en genvej til det. I stedet for at skrive React.Fragment, kan du bare bruge et tomt tag <> .

Mulighed # 3 +

ReactDOM.render (
  <>
    
    
  ,
  mountNode
);

Det tomme tag bliver transpileret til syntaksen React.Fragment. Jeg bruger denne syntaks til at fortsætte med eksemplet.

Du skal dog altid prøve at gøre det første argument til ReactDOM.render et enkelt komponentopkald i stedet for det indlejrede træ, som vi lige har gjort. Dette er i det væsentlige en kodekvalitetspræference. Det tvinger dig til at tænke over dine komponenter hierarki, navne og forhold. Lad os gøre det næste.

Komponenten på øverste niveau

Lad os introducere en komponent på topniveau, der er vært for både knappen og displaykomponenterne. Spørgsmålet er nu: hvad skal vi navngive denne nye overordnede komponent?

Tro det eller ej, at navngive dine komponenter og deres tilstand / rekvisitterelementer er en meget hård opgave, der vil påvirke den måde, disse komponenter fungerer og udfører. De rigtige navne tvinger dig til de rigtige designbeslutninger. Tag dig tid og tænk over ethvert nyt navn, du introducerer til dine React-apps.

Da denne nye overordnede komponent er vært for en skærm med en knap, der øger det viste antal, kan vi tænke på det som tællerværdighedsstyring. Lad os navngive det CountManager.

const CountManager = () => {
  Vend tilbage (
    <>
      
      
    
  );
};
ReactDOM.render (, mountNode);

Da vi vil vise tællets værdi i den nye Display-komponent, behøver vi ikke længere at vise tællets værdi som etiketten på knappen. I stedet kan vi ændre etiketten til noget som "+1".

const-knap = () => {
  Vend tilbage (
     console.log ('TODO: stigningstæller')}>
      1
    
  );
};

Bemærk, at jeg også har fjernet statuselementet fra knappen-komponenten, fordi vi ikke kan have det der længere. Med det nye krav har både knap- og displaykomponenter adgang til tælletilstandselementet. Skærmkomponenten viser den, og knapkomponenten opdaterer den. Når en komponent skal have adgang til et statselement, der ejes af dets søskendel, er en løsning at "løfte" det statale element et niveau op og definere det inde i deres overordnede komponent. I dette tilfælde er overordnede CountManager-komponenten, vi lige har introduceret.

Ved at flytte staten til CountManager, kan vi nu "flyde" data fra forælder til barn ved hjælp af komponentrekvisita. Det er hvad vi skal gøre for at få tælleværdien i displaykomponenten:

const Display = ({indhold}) => (
  
 {indhold} 
);
const CountManager = () => {
  const [count, setCount] = useState (0);
  Vend tilbage (
    <>
      
      
    
  );
};
ReactDOM.render (, mountNode);

Bemærk, hvordan jeg i CountManager brugte nøjagtigt den samme useState-linje, der var i knappen-komponenten. Vi løfter det samme tilstandselement. Bemærk også, hvordan jeg brugte et andet navn til det (indhold), da jeg flydede tællerværdien ned til skærmkomponenten via en rekvisit. Det er normalt. Du behøver ikke bruge det nøjagtige samme navn. Faktisk er det i nogle tilfælde bedre at introducere nye generiske navne bedre for børn, fordi de gør dem mere genanvendelige. Skærmkomponenten kunne genbruges til at vise andre numeriske værdier udover antallet.

Forældrekomponenter kan også flyde adfærd til deres børn, hvilket er, hvad vi skal gøre næste.

Da elementet for tælletilstand nu er i CountManager-komponenten, har vi brug for en funktion på det niveau til at håndtere opdatering af det. Lad os navngive denne funktion incrementCounter. Logikken for denne funktion er faktisk den samme logik, vi havde før i handleClick-funktionen i knappen-komponenten. Den nye incrementCounter-funktion vil opdatere CountManager-komponenttællestatusen for at øge værdien ved hjælp af den forrige værdi:

const CountManager = () => {
  // ....
  const incrementCounter = () => {
    setCount (count + 1);
  }
  // ...
};

OnClick-behandleren i knappen-komponenten skal nu ændres. Vi ønsker, at den skal udføre incrementCounter-funktionen, der findes i CountManager-komponenten, men en komponent kan kun få adgang til sine egne funktioner. Så for at gøre Button-komponenten i stand til at påkalde incrementCounter-funktionen i CountManager-komponenten, kan vi sende en henvisning til incrementCounter til Button-komponenten som en prop. Ja, rekvisitter kan også indeholde funktioner, ikke kun data. Funktioner er kun objekter i JavaScript, og ligesom objekter kan du videregive dem rundt.

Vi kan navngive denne nye prop noget. Jeg navngiver det clickAction og giver det en værdi af incrementCounter, som er henvisningen til den funktion, vi definerede i CountManager-komponenten. Vi kan bruge denne nye udleverede opførsel direkte som onClick-handlerværdi. Det vil være en prop til knappen-komponenten:

const-knap = ({clickAction}) => {
  Vend tilbage (
    
      1
    
  );
};
// ...
const CountManager = () => {
  // ...
  Vend tilbage (
    
                     ); };

Der sker noget meget magtfuldt her. Denne clickAction-egenskab tillader, at knapkomponenten påberåber CountManager-komponentens incrementCounter-funktion. Det er som når vi klikker på den knap, når knappen-komponenten ud til CountManager-komponenten og siger: "Hej forælder, gå videre og påberåbe os den stigningstælleradfærd nu".

I virkeligheden er CountManager-komponenten den, der kontrolleres her, og Button-komponenten følger bare generiske regler. Hvis du analyserer koden, som den er nu, vil du indse, hvordan knappen-komponenten ikke har nogen anelse om, hvad der sker, når den bliver klikket. Det følger bare de regler, der er defineret af overordnede, og påkalder en generisk clickAction. Forælderen styrer, hvad der går ind i den generiske adfærd. Dette følger begrebet isolationsansvar. Hver komponent her har visse opgaver, og de får fokus på det.

Se på skærmkomponenten for et andet eksempel. Fra dens synspunkt er tællerværdien ikke en tilstand. Det er bare en prop, at CountManager-komponenten overgår til den. Displaykomponenten vil altid vise den rekvisita. Dette er også en adskillelse af ansvarsområder.

Som designer af disse komponenter kan du vælge deres ansvarsniveau. For eksempel kunne vi have taget ansvaret for at vise tællerværdiedelen af ​​selve CountManager-komponenten og ikke bruge en ny Display-komponent til det.

CountManager-komponenten har ansvaret for at styre tællestaten. Det er en vigtig designbeslutning, som vi har taget, og det er en, du bliver nødt til at tage meget i en React-applikation. Hvor skal man definere staten?

Den praksis, jeg følger, er at definere et tilstandselement i en delt forældreknudepunkt, der er så tæt som muligt på alle de børn, der har brug for adgang til det tilstandselement. For en lille applikation som denne betyder det normalt selve topniveaukomponenten. I større applikationer administrerer et undertræ muligvis sin egen "filial" i stedet for at stole på globale tilstandselementer, der er defineret på rodniveaukomponenten på øverste niveau.

Komponenten på øverste niveau bruges populært til at administrere delt applikationstilstand og handlinger, fordi den er overordnet til alle andre komponenter. Vær forsigtig med dette design, fordi opdatering af et tilstandselement på komponenten på øverste niveau betyder, at hele komponenttræet gendannes igen (i hukommelsen).

Her er den fulde kode til dette eksempel indtil videre:

// jsdrops.com/bx8
const-knap = ({clickAction}) => {
  Vend tilbage (
    
      1
    
  );
};
const Display = ({indhold}) => (
  
 {indhold} 
);
const CountManager = () => {
  const [count, setCount] = useState (0);
  const incrementCounter = () => {
    setCount (count + 1);
  };
  Vend tilbage (
    
                     ); };

Gør komponenter genanvendelige

Komponenter handler om genanvendelighed. Lad os gøre knapkomponenten genanvendelig ved at ændre den, så den kan øge det samlede antal med enhver værdi, ikke kun 1.

Lad os starte med at tilføje flere knapelementer i CountManager-komponenten, så vi kan teste denne nye funktion:

const CountManager = () => {
  // ..
  Vend tilbage (
    <>
      

Alle knapelementer, der er gengivet ovenfor, har i øjeblikket en +1-etiket, og de øger antallet med 1. Vi ønsker at få dem til at vise en anden etiket, der er specifik for hver knap, og få dem til at udføre en anden handling baseret på en bestemt værdi til hver af dem. Husk, at du kan videregive enhver værdi til et React-element som en prop.

Her er det brugergrænseflade, jeg har i tankerne, efter at have klikket på hver knap en gang:

I skærmbilledet ovenfor startede optællingen med 0. Jeg tilføjede 1, derefter 5 og derefter 10 for at komme til 16

Inden vi gennemgår denne øvelse, skal du tage lidt tid og tænke over den og forsøge at implementere den selv. Det er for det meste ligetil. Tip: du bliver nødt til at introducere 1 ny prop til knap. Giv det et skud. Kom tilbage, når du er klar til at sammenligne din løsning med min.

Tilføjelse af nye rekvisitter

Den første ting, vi skal gøre, er at gøre +1-etiketten i knappen-komponenten til en tilpassbar.

For at gøre noget, der kan tilpasses i en React-komponent, introducerer vi en ny prop (som den overordnede komponent kan kontrollere) og får komponenten til at bruge dens værdi. For vores eksempel kan vi få knappen til at modtage beløbet til trin (1, 5, 10) som en ny rekvisit. Jeg hedder det KlikValue. Vi kan ændre gengivelsesmetoden i CountManager for at videregive de værdier, vi vil teste med til denne nye prop.

Vend tilbage (
  <>
    

Bemærk et par ting indtil videre:

  • Jeg navngav ikke den nye ejendom med noget, der var relateret til tælling. Knapkomponenten behøver ikke være opmærksom på betydningen af ​​dens klikbegivenhed. Det er bare nødvendigt at videregive denne clickValue, når dens klikhændelse udløses. For eksempel ville navngivning af denne nye egenskabstælling ikke være det bedste valg, fordi vi nu i knapkomponenten læser koden for at forstå, at et knapelement er relateret til en tælling. Dette gør knapkomponenten mindre genanvendelig. Hvis jeg f.eks. Vil bruge den samme knapkomponent til at tilføje et brev til en streng, ville dens kode være forvirrende.
  • Jeg brugte krøllede parenteser til at videregive værdierne for den nye egenskab clickValue (clickValue = {5}). Jeg brugte ikke strenge der (clickValue = "5"). Da jeg har en matematisk operation med disse værdier (hver gang der klikkes på en knap), har jeg brug for, at disse værdier er tal. Hvis jeg videregiver dem som strenge, bliver jeg nødt til at foretage en række-til-nummer-konvertering, når tilføjelsesprocessen skal udføres.
At videregive et nummer som en streng er en almindelig fejl i React. Se denne artikel for flere reaktionsrelaterede almindelige fejl.

Tilpasning af adfærd

Den anden ting, vi har brug for at gøre generiske i CountManager-komponenten, er incrementCounter-funktionen. Det kan ikke have en hardkodet count + 1-operation, som den gør nu. Ligesom hvad vi gjorde for Button-komponenten, for at gøre en funktion generisk, får vi den til at modtage et argument og bruge argumentets værdi. For eksempel:

incrementCounter = (incrementValue) => {
  setCount (count + incrementValue);
};

Nu skal vi bare gøre, at knappen-komponenten bruger sin clickValue-prop som sin etiket og får den til at påberåbe sig sin onClick-handling med sin clickValue som et argument.

const-knap = ({clickValue, clickAction}) => {
  Vend tilbage (
     clickAction (clickValue)}>
      + {ClickValue}
    
  );
};

Bemærk, hvordan jeg var nødt til at pakke onClick-propen med en inline-pilfunktion for at gøre det bundet til knapens clickValue. JavaScript-lukning for denne nye pilefunktion vil tage sig af det.

De tre knapper skal nu øges med deres tre forskellige klikværdier. Du kan se dette eksemples kode på jsdrops.com/bx9.

Accepter input fra brugeren

Forestil dig, at vi er nødt til at tælle de tegn, en bruger skriver i et tekstområde, ligesom Twitter's tweetform. For hver karakter brugeren bruger, vi har brug for at opdatere brugergrænsefladen med det nye antal tegn.

Her er en komponent, der viser et tekstindholdselement med en pladsholderdel for tegntællingen:

// jsdrops.com/bx10
const CharacterCounter = () => {
  Vend tilbage (