React

React on Metan (Facebook) kehittämä javascript-kirjasto käyttöliittymien tekemiseen.

Tämä sivu käyttää funktiokomponentteja ja koukkuja. Jos haluat perusteellisemman esityksen koukuista, katso React ja koukut.

Kannattaa katsoa myös React Functional Components, Props, and JSX.

JSX

Hello world

Yksinkertaisimmassa React-ohjelmassa liitetään html-dokumenttiin react-kirjasto, Babel JSX-muunnosta varten, ja kirjoitetaan script-elementin sisään oma komponentti ja sen renderöinti. Huomaa, että script-elementin type-attribuutille on annettava arvo: Babel muuntaa vain skriptit, joiden tyyppi on text/babel tai text/jsx. Tavallinen javascript-skripti jätetään rauhaan.

Kts. Quick Start ja Tutorial.

<!DOCTYPE html>
<html lang="fi">
  <head>
    <meta charset="utf-8" />
    <title>Hello World</title>

    <!-- Babel muuntaa JSX:n lennossa. React itse tuodaan skriptissä. -->
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  </head>
  <body>
    <div id="main"></div>

    <script type="text/babel" data-type="module">
// React 19 ladataan ESM-muodossa. React-oletustuonti tarvitaan, koska Babel
// kääntää JSX:n React.createElement-kutsuiksi.
import React from "https://esm.sh/react@19";
import { createRoot } from "https://esm.sh/react-dom@19/client";

// Yksinkertainen komponentti, joka palauttaa tekstikappaleen.
// Komponentille annetut parametrit löytyvät props-parametrista.
const HelloWorld = function (props) {
  return (
    <p>
      Hello {props.name}!
    </p>
  );
};

// Näytetään main-tunnisteella varustetun lohkon sisällä edellä luotu komponentti.
// Esitettävä nimi annetaan attribuuttina.
const juuri = createRoot(document.getElementById("main"));

juuri.render(<HelloWorld name="Tommi" />);
    </script>
  </body>
</html>

Kaksi yksityiskohtaa kannattaa huomata. data-type="module" kertoo Babelille, että skriptissä käytetään import-lauseita. Ja React ladataan esm.sh-palvelusta ESM-muodossa, koska React 19:stä lähtien ei enää julkaista script-elementillä ladattavia UMD-paketteja. Vanhoissa esimerkeissä näkee vielä muodon

<script crossorigin="anonymous" src="https://unpkg.com/react@18/umd/react.development.js"></script>

joka toimii vain React 18:aan asti.

Funktiokomponentit

const Komponentti = function (props) {
  return (<p>Hello World!</p>);
};

React ja silmukat

JSX-koodiin voi kirjoittaa vain React-elementtejä, html-elementtejä sekä aaltosulkeiden sisään lausekkeita: funktiokutsuja, laskutoimituksia ja muuttujien nimiä. for-silmukka on lause eikä lauseke, joten se on suoritettava erikseen ennen palautusta. Sisäkkäisistä silmukoista sisempi siirretään omaan funktioonsa tai komponenttiinsa.

const Test = function () {
  const taulukko = ["foo", "bar", "foobar", "barfoo"];
  const taulukko2 = [1, 2, 3, 4];
  let aputaulukko = [];

  // JSX-koodissa voi olla vain lausekkeita, ja taulukko[i] on lauseke.
  // Muista antaa jokaiselle riville uniikki key-attribuutti.
  for (let i = 0; i < taulukko.length; i++) {
    aputaulukko.push(<li key={taulukko[i]}>{taulukko[i]}</li>);
  }

  aputaulukko = [];

  // Entä jos silmukoita on kaksi? Sisempi siirretään omaan komponenttiinsa.
  for (const ulompi of taulukko2) {
    aputaulukko.push(<Apukomponentti key={ulompi} taulukko={taulukko} ulompi={ulompi} />);
  }

  return (
    <ul>
      {aputaulukko}
    </ul>
  );

  /* Tämä EI toimi, vaan antaa unexpected token -virheen: for on lause, ei lauseke.
  return (
    <ul>{
      for (let i = 0; i < taulukko.length; i++) {
        aputaulukko.push(<li>{taulukko[i]}</li>);
      }
    }</ul>
  );
  */
};


const Apukomponentti = function (props) {
  const aputaulukko = [];

  for (const alkio of props.taulukko) {
    aputaulukko.push(<li key={alkio}>{alkio}</li>);
  }

  return (
    <li>
      {props.ulompi}
      <ul>
        {aputaulukko}
      </ul>
    </li>
  );
};

Käytännössä silmukka kirjoitetaan yleensä map-metodilla, joka on lauseke ja mahtuu siksi suoraan JSX:n sisään:

return (
  <ul>
    {taulukko.map((alkio) => <li key={alkio}>{alkio}</li>)}
  </ul>
);

Älä käytä silmukan indeksiä key-arvona, jos listan järjestys voi muuttua tai siitä voidaan poistaa alkioita. Avaimen tehtävä on kertoa Reactille mikä rivi on mikäkin, ja indeksi muuttuu järjestyksen mukana. Kts. Rendering lists.

Lomakkeet

Reactissa lomakkeet ovat tavallisesti kontrolloituja: kentän arvo tulee komponentin tilasta, ja jokainen muutos päivittää tilan. Käytä kontrolloitua lomaketta aina kun mahdollista. Joissakin tilanteissa lomakkeesta voi tehdä myös kontrolloimattoman, jolloin arvot luetaan vasta lähetettäessä.

Lomakkeissa voi käyttää samoja tarkistuksia ja virheilmoituksia kuin muutenkin. Kts. validointiesimerkki.

Komponentin tila

Tila ei päivity heti

Huom. Tilamuuttuja on kyseisen renderöinnin ajan vakio. Sen arvo ei muutu asetusfunktion kutsumisesta, vaan vasta seuraavassa renderöinnissä. Seuraava tulostaa siis vanhan arvon:

setLaskuri(laskuri + 1);
console.log(laskuri);        // vanha arvo, ei kasvatettu

Samasta syystä peräkkäiset kasvatukset eivät kerry:

setLaskuri(laskuri + 1);
setLaskuri(laskuri + 1);     // molemmat laskevat samasta vanhasta arvosta
                             // -> laskuri kasvaa yhdellä, ei kahdella

Jos uusi arvo riippuu edellisestä, anna asetusfunktiolle funktio:

setLaskuri((edellinen) => edellinen + 1);
setLaskuri((edellinen) => edellinen + 1);   // nyt kasvaa kahdella

Kts. State as a snapshot ja Queueing a series of state updates.

Monimutkainen tila

Huom. Jos tallennat tilaan objekteja tai taulukoita, ole tarkkana kopioinnissa. Esimerkki muuttuvasta ja muuttumattomasta tilasta (mutable.js) näyttää konkreettisesti, mitä tapahtuu kun tilaa muutetaan paikallaan: alkio menee tilaan mutta ei ilmesty näkyviin.

Kts. myös Updating objects in state ja Updating arrays in state.

Jos tarvitset komponentissa muuttujia, jotka eivät vaikuta renderöintiin, niitä ei tarvitse säilyttää tilassa. Tavallinen muuttuja riittää — tai useRef, jos arvon on säilyttävä renderöintien yli.

Fetch ja useEffect

Perusteellisempi käsittely löytyy sivulta React ja koukut.

Lisätietoa

Käyttäjien kommentit

Kommentoi Lisää kommentti