Komponenttien pitäminen puhtaina

Jotkin JavaScript funktiot ovat puhtaita. Puhtaat funktiot suorittavat ainoastaan laskelman eikä mitään muuta. Kirjoittamalla komponenttisi puhtaina funktioina voit välttää kokonaisen luokan hämmentäviä bugeja ja arvaamatonta toimintaa koodipohjasi kasvaessa. Saadaksesi nämä hyödyt, on muutamia sääntöjä joita seurata.

Tulet oppimaan

  • Mitä puhtaus on ja miten se auttaa bugien välttämisessä
  • Miten komponentteja pidetään puhtaina pitämällä muutokset poissa renderöintivaiheesta
  • Miten käyttää Strcit Modea etsimään virheitä komponenteistasi

Puhtaus: Komponenttisi kaavoina

Tietojenkäsittelytieteessä (ja etenkin funktionaalisen ohjelmoinnin maailmassa), puhdas funktio on funktio seuraavilla ominaisuuksilla:

  • Huolehtii omista asioistaan. Se ei muuta yhtään oliota tai muuttujaa joka oli olemassa ennen kuin sitä kutsuttiin.
  • Samat sisääntulot, samat ulostulot. Annettaen samat lähtötiedot, puhtaan funktion tulisi aina palauttaa sama lopputulos.

Saatat ehkä jo tietää yhden esimerkin puhtaista funktioista: kaavat matematiikassa.

Harkitse tätä matemaattista kaavaa: y = 2x.

Jos x = 2 silloin y = 4. Aina.

Jos x = 3 silloin y = 6. Aina.

Jos x = 3, y ei joskus ole 9 tai –1 tai 2.5 riippuen kellonajasta taikka markkinatalouden tilasta.

Jos y = 2x ja x = 3, y on aina 6.

Jos tästä tehtäisiin JavaScript funktio, se voisi näyttää tältä:

function double(number) {
return 2 * number;
}

Yllä olevassa esimerkissä, double() on puhdas funktio. Jos välität sille 3, se palauttaa 6. Aina.

React on suunniteltu tämän konseptin ympärille. React olettaa, että jokainen komponentti jonka kirjoitat on puhdas funktio. Tämä tarkoittaa, että kirjoittamasi React komponenttien täytyy palauttaa aina sama JSX:n kun annetaan samat lähtötiedot:

function Recipe({ drinkers }) {
  return (
    <ol>    
      <li>Boil {drinkers} cups of water.</li>
      <li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li>
      <li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li>
    </ol>
  );
}

export default function App() {
  return (
    <section>
      <h1>Spiced Chai Recipe</h1>
      <h2>For two</h2>
      <Recipe drinkers={2} />
      <h2>For a gathering</h2>
      <Recipe drinkers={4} />
    </section>
  );
}

Kun välität drinkers={2} komponentille Recipe, se palauttaa JSX:n sisältäen 2 cups of water. Aina.

Kun välität drinkers={4}, se palauttaa JSX:n sisältäen 4 cups of water. Aina.

Juuri kuten matemaattinen kaava.

Voit ajatella komponenttisi reseptinä: jos seuraat sitä, etkä esittele uusia ainesosia kesken ruoanlaiton aikana, saat saman aterian joka kerta. Tuo “ateria” on JSX jonka komponentti tarjoaa Reactille renderöitäväksi.

Tee resepti x määrälle henkilöitä: ota x kuppia vettä, lisää x lusikallista teetä ja 0.5x lusikallista mausteita ja 0.5x kuppia maitoa

Illustrated by Rachel Lee Nabors

Sivuvaikutukset: (ei-)toivotut seuraukset

Reactin renderöintiprosessin on aina oltava puhdas. Komponenttien täytyisi palauttaa vain niiden JSX eikä muuttaa yhtään oliota tai muuttujia, jotka olivat olemassa ennen renderöintiä—se tekisi niistä epäpuhtaita!

Tässä komponentti joka rikkoo tätä sääntöä:

let guest = 0;

function Cup() {
  // Huonoa: muuttaa olemassa olevaa muuttujaa!
  guest = guest + 1;
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup />
      <Cup />
      <Cup />
    </>
  );
}

Tämä komponentti lukee ja kirjoittaa guest muuttujaan, joka on määritelty sen ulkopuolella. Tämä tarkoittaa, että komponentin kutsuminen useita kertoja tuottaa eri JSX:ää! Lisäksi jos muut komponentit lukevat guest muuttujaa, nekin tuottavat eri JSX:ää myös, riippuen milloin ne renderöitiin. Tämä ei ole ennustettavissa.

Palataan kaavaamme y = 2x, nyt jos x = 2, emme voi luottaa, että y = 4. Testimme epäonnistuisi, käyttäjämme olisivat hämillään, lentokoneita tippuisi taivaalta—näet miten tämä voisi johtaa sekaviin bugeihin!

Voit korjata tämän komponentin välittämällä guest muuttujan propsina:

function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup guest={1} />
      <Cup guest={2} />
      <Cup guest={3} />
    </>
  );
}

Nyt komponenttisi on puhdas, sillä JSX joka palautetaan riippuu ainoastaan guest propista.

Yleisesti ottaen sinun ei tarvitse olettaa komponenttien renderöitävän missään tietyssä järjestyksessä. Sillä ei ole väliä kutsutko y = 2x ennen vai jälkeen y = 5x: molemmat kaavat toimivat erikseen toisistaan. Samalla tavalla, jokaisen komponentin tulisi “miettiä itselleen”, eikä koordinoida tai riippua muista renderöinnin aikana. Renderöinti on kuin koulun koe: jokaisen komponentin tulisi laskea JSX itsekseen!

Syväsukellus

Epäpuhtaiden laskelmien tunnistaminen StrictModella

Vaikka et välttämättä ole käyttänyt niitä kaikkia vielä, Reactissa on kolmenlaista syötettä jota lukea renderöinnin aikana: propsit, tila, ja konteksti. Kannattaa aina kohdella näitä arvoja vain-luku muodossa.

Kun haluat muuttaa jotain vastauksena käyttäjän syötteeseen, tulisi asettaa tila muuttujan kirjoittamisen sijaan. Sinun ei tulisi koskaan muuttaa olemassa olevia muuttujia tai olioita kun komponenttisi renderöityy.

React tarjoaa “Strict Mode”:n jolloin se kutsuu jokaisen komponentin funktiota kahdesti kehityksen aikana. Kutsumalla komponentin funktiota kahdesti, Strict Mode auttaa etsimään komponentteja, jotka rikkovat näitä sääntöjä.

Huoma miten alkuperäinen esimerkki näytti “Guest #2”, “Guest #4”, ja “Guest #6” seuraavien “Guest #1”, “Guest #2”, ja “Guest #3” sijasta. Alkuperäinen funktio oli epäpuhdas, joten sen kahdesti kutsuminen rikkoi sen. Korjattu puhdas versio toimii vaikka jos funktiota kutsuttaisiin kahdesti joka kerta. Puhtaat funktiot vain laskevat, joten niiden kutsuminen kahdesti ei muuta yhtään mitään—juuri kuten double(2) kutsuminen kahdesti ei muuta mitä palautetaan, eikä kaavan ratkaisu y = 2x kahdesti muuta mitä y on. Samat lähtötiedot, samat lopputiedot. Aina.

Strict Modella ei ole vaikutusta tuotannossa, joten se ei hidasta sovellusta käyttäjillesi. Ottaaksesi Strict Moden käyttöön, voit kääriä pääkomponenttisi <React.StrictMode> sisään. Jotkin kehykset tekevät tämän oletuksena.

Paikallinen mutaatio: Komponenttisi pieni salaisuus

Yllä olevassa esimerkissä, ongelma oli, että komponentti muutti olemass olevaa muuttujaa kesken renderöinnin. Tätä kutsutaan usein “mutaatioksi” kuulostaakseen pelottavemmalta. Puhtaat funktiot eivät mutatoi muuttujia funktion käyttöalueen ulkopuolella tai olioita jotka olivat luotuna ennen kutsua—se tekee niistä epäpuhtaita!

Kuitenkin, on täysin sallittua muuttaa muuttujia ja olioita, joita olet juuri luonut renderöinnin aikana. Tässä esimerkissä, luot [] taulukon ja määrität sen cups muuttujaan ja sitten push metodia käyttäen lisäät tusinan kuppia siihen:

function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaGathering() {
  let cups = [];
  for (let i = 1; i <= 12; i++) {
    cups.push(<Cup key={i} guest={i} />);
  }
  return cups;
}

Jos cups muuttuja tai [] taulukko olivat luotuna TeaGathering funktion ulkopuolella, tämä olisi iso ongelma! Muuttaisit olemassa olevaa oliota sijoittamalla kohteita siihen taulukkoon.

Kuitenkin se on sallittua, koska olet luonut ne saman renderöinnin aikana TeaGathering:n sisällä. Koodi TeaGathering:n ulkopuolla ei koskaan tiedä tämän tapahtuneen. Tätä kutsutaan “paikalliseksi mutaatioksi”—se on kuin komponenttisi pieni salaisuus.

Missä voit aiheuttaa sivuvaikutuksia

Vaikka funktionaalinen ohjelmointi nojaa pitkälti puhtauteen, jossain vaiheessa, jossain, jonkin on muututtava. Tämähän on koodauksen koko pointti! Nämä muutokset—ruudunpäivitykset, animaation aloitukset, datan muuttaminen—ovat sivuvaikutuksia. Ne ovat asioita, jotka tapahtuvat “siinä sivussa,” ei kesken renderöinnin.

Reactissa, sivuvaikutukset useimmiten kuuluvat tapahtumakäsittelijöiden sisään. Tapahtumakäsittelijät ovat funktioita, joita React suorittaa kun teet jotain toimintoja—esimerkiksi painat nappia. Vaikka tapahtumakäsittelijät on määritelty komponentin sisällä, niitä ei suoriteta renderöinnin aikana! Joten tapahtumakäsittelijöiden ei tarvitse olla puhtaita.

Jos olet olet käyttänyt kaikki vaihtoehdot, etkä löydä oikeaa tapahtumakäsittelijää sivuvaikutuksellesi, voit silti kiinnittää sen palautettuun JSX:ään käyttäen useEffect kutsua komponentissasi. Tämä kertoo Reactille, että kutsuu sitä myöhemmin renderöinnin jälkeen, jolloin sivuvaikutukset ovat sallittuja. Huomaa, että tämän tavan pitäisi olla sinun viimeinen keino.

Kun mahdollista, kokeile muotoilla logiikkasi vain renderöinnillä. Yllätyt miten pitkälle sillä pääsee!

Syväsukellus

Miksi React välittää puhtaudesta?

Puhtaiden funktioiden kirjoittaminen vaatii tottumusta ja itsekuria. Mutta se avaa mahtavia mahdollisuuksia:

  • Komponenttisi voidaan suorittaa eri ympäristössä—esimerkiksi palvelinpuolella. Sillä ne palauttaa saman tuloksen samoista lähtötiedoista, yksi komponentti voi palvella monta käyttäjäpyyntöä.
  • Voit parantaa tehokkuutta ohittamalla renderöinnin komponenteille, joiden lähtötiedot eivät ole muuttuneet. Tämä on turvallista koska puhtaat funktiot palauttavat aina saman lopputuloksen, joten ne on turvallista tallentaa.
  • Jos jokin data muuttuu kesken renderöinnin syvällä komponenttipuussa, React voi aloittaa renderöinnin uudelleen hukkaamatta aikaa keskeneräiseen renderöintiin. Puhtaus tekee keskeyttämisestä turvallista.

Jokainen uusi Reactin ominaisuus joita rakennamme hyödyntää puhtautta. Tiedonhausta animaatioihin ja tehokkuuteen, komponenttien pitäminen puhtaina avaa tehokkaan React paradigman.

Kertaus

  • Komponentin on oltava puhdas, tarkoittaen:
    • Pitää huoli sen omista asioistaan. It should not change any objects or variables that existed before rendering.
    • Samat sisääntulot, sama ulostulo. Annettaen sama syöte, komponentin tulisi aina palauttaa sama JSX.
  • Renderöinti voi tapahtua koska vain, joten komponenttien ei tulisi riippua toistensa renderöintijärjestyksestä.
  • Sinun ei pitäisi muuttaa lähtötietoja, joita komponenttisi käyttää renderöintiin. Tämä sisältää propsit, tilan sekä kontekstin. Ruudun päivittämiseksi “aseta” tila olemassaolevien olioiden muuttamisen sijaan.
  • Pyri ilmaisemaan komponenttisi logiikka JSX:ssä jota palautat. Kun täytyy “muuttaa asioita”, useimmiten teet sen tapahtumakäsittelijässä. Viimeisenä keinona voit käyttää useEffect:ia.
  • Puhtaiden funktioiden kirjoittaminen vaatii hieman harjoittelua, mutta se avaa Reactin paradigman voiman.

Haaste 1 / 3:
Korjaa rikkinäinen kello

Tämä komponentti yrittää asettaa <h1>:n CSS luokan arvoksi "night" keskiyöstä aamu kuuteen ja arvoksi "day" muina aikoina. Se ei kuitenkaan toimi. Voitko korjata tämän komponentin?

Voit tarkistaa onko ratkaisusi toimiva tilapäisesti muuttamalla tietokoneesi aikavyöhykettä. Kun nykyinen aika on keskiyön ja aamu kuuden välillä, kellon tulisi omata käänteiset värit!

export default function Clock({ time }) {
  let hours = time.getHours();
  if (hours >= 0 && hours <= 6) {
    document.getElementById('time').className = 'night';
  } else {
    document.getElementById('time').className = 'day';
  }
  return (
    <h1 id="time">
      {time.toLocaleTimeString()}
    </h1>
  );
}