State-tilamuuttujat
Tähän mennessä olemme toteuttaneet staattisen ulkoasun, joka ei reagoi käyttäjän toimintaan oikeastaan muuten kuin pienellä animaatiolla, kun sitruunaa napautetaan.
Jotta käyttäjän napautukset voidaan tallentaa, niin sitä varten Reactissa on oma muuttujarakenteensa, joka säilyttää muuttujan arvon renderöintien välillä. Reactissa tietoja ei pysty tallentamaan "tavalliseen" JavaScript-muuttujaan, sillä komponentteja kutsutaan aina sivun päivityssyklin yhteydessä.
Toteutetaan ensin ohjelmakoodi, joka tallentaa käyttäjän napautusten lukumäärän. Muokkaa src-kansiossa olevaa App.jsx-tiedostoa. Lisää ensin tiedoston alkuun seuraava ohjelmarivi:
import { useState } from 'react';
Tämä import-tuonti poikkeaa aikaisemmista siinä, että react
-nimen edessä ei ole ./
-merkkejä. Aikaisemmissa tuonti tapahtui projektin tiedostoista (siksi kansioviittaus). Tällä kerralla tuodaan toiminnallisuus npm-pakettihallinnan kautta asennetusta react-paketista, jolloin viitataan paketin nimeen.
Aaltosulkeillakin on tärkeä rooli, niiden sisällä määritellään, minkä niminen kokonaisuus react-paketista tuodaan. React-paketti sisältää lukuisan määrän erilaisia toiminnallisuuksia ja selkeyden vuoksi ohjelmakoodiin tuodaan ainoastaan ne, jotka ovat tarpeellisia. Todellisuudessa kehitysympäristö tuo automaattisesti react-paketista sinne määritellyn ns. oletuskokonaisuuden (default) ja käyttää sitä. Sitä ei tarvitse tuoda erikseen jokaiselle sivulle, sen tuonti hoidetaan automaattisesti.
useState-funktio on yksi eniten käytetyistä react-paketissa olevista toiminnallisuuksista. Se on osa Reactin koukkuja (React Hooks), joiden avulla on sovellusta on helpompi toteuttaa. useState mahdollistaa jonkin tallennetun tiedon säilyttämisen sivurenderöintien välillä. Luodaan sovellukseen uusi tilamuuttuja useStaten avulla, lisää funktion alkuun seuraava ohjelmarivi:
// Luodaan tilamuuttuja, jossa tallennetaan napautusten määrä.
const [clicks, setClicks] = useState(0);
Tämä luo uuden tilamuuttujan alkuarvolla 0. useState-funktio palauttaa kaksialkioisen taulukon, jossa ensimmäinen alkio on muuttuja, josta pystyy lukemaan tilamuuttujan sen hetkisen arvon. Toinen alkio on funktio, jolla tilamuuttujan arvon voi päivittää uuteen. Funtion palautusarvoina tuleva tilamuuttuja ja päivitysfunktio voidaan nimetä täysin vapaasti, mutta yleinen tapa on, että päivitysfunktion nimen alussa on set
-sana ja loppuosa on tilamuuttujan nimi, kuten esimerkiksi [values, setValues]
ja [visible, setVisible]
.
Välitetään seuraavaksi tämä tilamuuttujan arvo Balance-komponentille. Muuta return-lauseessa Balance-komponentin kutsurivi seuraavanlaiseksi:
<Balance total={clicks} />
Nyt Balance-komponentille välitetään tilamuuttujan arvo. Voit testata tätä muuttamalla useState-kutsun yhteydessä annettua arvoa eli sulkeiden välissä olevaa lukua. Jos teet muutoksen ja tallennat, niin sitruunoiden lukumäärä muuttuu vastaavasti sovelluksessa.

Lisätään seuraavaksi käsittelijä, joka suoritetaan, kun määriteltyä elementtiä napautetaan. Lisää funktion alkuun, useState-lauseen jälkeen, seuraava ohjelmakoodi:
const handleClick = () => {
// Kasvatetaan napautusten määrää yhdellä.
setClicks(clicks + 1);
}
Tämä esittelee handleClick
-nimisen funktion, joka hakee tilamuuttujan clicks
sen hetkisen arvon, lisää siihen yhden ja tallentaa yhteenlaskun tuloksen tilamuuttujan uudeksi arvoksi setClicks
-funktiokutsulla.
Demotaan ensin käsittelijän toimintaa, lisää seuraava rivi render-lauseen sisälle, ennen Balance-komponentin kutsua.
<button onClick={handleClick}>napauta</button>
Nyt voit testata muuttujan toimintaa napauttamalla selaimen ruudulle ilmestynyttä napauta-nappia.
- Kun napautat nappia kerran, kasvaa ruudulla oleva arvo yhdellä.
- Kun päivität selainikkunan, luku nollautuu. Päivitys käynnistää sovelluksen uudelleen, jolloin tilamuuttujan arvo alustetaan.

Seuraavaksi pitäisi saada napautettava sitruunan puolikas reagoimaan napautuksiin. Tämä tapahtuu oikeastaan ihan samalla tavalla kuin edellä toteutettu testinappi. Erona on oikeastaan se, että Lemon-komponentti ei osaa automaattisesti käsitellä tapahtuvaa klikkausta, vaan se pitää toteuttaa ensin.
Muuta src/components-kansiossa olevan Lemon.jsx-komponentin ohjelmakoodi seuraavanlaiseksi:
import lemon from '../assets/lemon-big.svg'
function Lemon(props) {
return (
<div className="lemon">
<img src={lemon} alt="lemon" onClick={props.onClick} />
</div>
);
}
export default Lemon;
Funktiomäärittelyyn tuli kaksi pientä muutosta. Funktio saa parametrinaan props
-taulukon, jolloin päästään kiinni komponentin kutsun yhteydessä välitettyihin arvoihin. Toiseksi kuvalle lisättiin onClick
-käsittelijä, joka kytketään propsien kautta välitettyyn funktioon.
Tämän jälkeen voit muokata App.jsx-tiedoston render-lausetta. Poista edellä lisätty button
-testinappi ja muuta Lemon-komponentin kutsu seuraavanlaiseksi:
<Lemon onClick={handleClick} />
Tämän muutoksen jälkeen sitruunan napauttaminen kasvattaa lukumäärää ihan samalla tavalla kuin testinapin napauttaminen aikaisemmin.
Nyt sovelluksessa on jo hyvin yksinkertainen clicker-pelin toiminnallisuus. Sitruunaa napauttamalla saa kasvatettua lukumäärää yhdellä. Jotta sitruunoiden kerääminen olisi mielekästä, niin hankituilla sitruunoilla pitäisi pystyä lunastamaan päivityksiä. Ennen kuin voimme toteuttaa sitä, on sovellukseen toteutettava navigointi.
Muutosten vienti versiohallintaan
Viedään viimeisimmät muutokset versiohallintaan.
git add .
git commit -m "lisää useState-muuttujan napautuksille"