React Hooks -funktiot

Olemme aikaisemmin käyttäneet tämän projektin aikana useamman kerran useState-funktiota, joka on React-kirjaston eniten käytetty Hooks-funktio.

Hooks-funktioiden tavoitteena on paketoida jokin usein tarvittava toiminnallisuus yhdeksi kokonaisuudeksi, jota voi sitten hyödyntää aina kun on tarve.

Hooksit ovat oikeastaan ihan tavallisia JavaScript-funktioita, joilla on kaksi lisäsääntöä:

  • Hooks-funktioita saa kutsua ainoastaan koodin päätasolla eli niitä ei saa kutsua silmukoiden tai ehtorakenteiden sisältä.
  • Hooks-funktioita saa kutsua ainoastaan Reactin (funktio)komponenteista tai itse tehdyistä Hooks-funktioista.

Näiden lisäksi Hooks-funktiot tulee nimetä niin, että ne alkavat sanalla use.

useLocalStorage-funktion esittely

Seuraavaksi toteutamme oman Hooks-funktion, joka hoitaa taustalle tietojen säilömisen. Lisää src/utils-kansioon uusi tiedosto, anna sen nimeksi useLocalStorage.js ja liitä sen sisällöksi seuraava ohjelmakoodi, joka pohjautuu Using localStorage with React Hooks-sivulla olevaan koodiin.

import { useEffect, useState } from "react";

// Muuttaa muuttujan JSON-merkkijonoksi.
const decode = (value) => {  
  return JSON.stringify(value);
}

// Purkaa JSON-merkkijonon muuttujaksi.
const encode = (value) => {
  return JSON.parse(value);
}

const useLocalStorage = (key, defaultState) => {

  // Tilamuuttujan määrittely, arvoksi haetaan joko
  // localStorage-muuttujan arvo tai alkuarvo.
  const [value, setValue] = useState(
    encode(localStorage.getItem(key) || null) || defaultState
  );

  // Tallennetaan tilamuuttuja localStorageen aina,
  // kun arvo muuttuu.
  useEffect(() => {
    localStorage.setItem(key, decode(value));
  },  [value]);

  // Alkuarvojen palautusfunktio.
  const resetValue = () => {
    setValue(defaultState);
  }

  return [value, setValue, resetValue];
}

export default useLocalStorage;

Aivan ensimmäiseksi tuodaan React-kirjastosta useState- ja useEffect-funktiot, joita koodissa tullaan hyödyntämään.

Esitellään decode- ja encode-apufunktiot, joita käytetään muuttamaan ja purkamaan localStorage-taltioon tallennettavaa tietoa. Koska localStorage-taltioon voi talletaan ainoastaan merkkijonoja, on esimerkiksi stats-oliomuuttuja ja storeitems-taulukkomuuttuja muunnettava merkkijonomuotoon. Tämä onnistuu funktioissa käytettävillä JSON.stringify- ja JSON.parse-funktioilla. Näistä ensimmäinen muuttaa olio- tai taulukkomuuttujan merkkijonomuotoiseksi, kun jälkimmäinen puolestaan purkaa merkkijonomuotoisen merkinnän olio- tai taulukkomuuttujaksi.

Seuraavaksi esitellään useLocalStorage-funktio, joka saa kutsuttaessa kaksi parametria: key ja defaultState.

Funktion ensimmäisenä vaiheena on tilamuuttujan alustaminen useState-funktiolla. Tilamuuttujan alkuarvoksi määritellään joko localStorage-taltioon tallennettu arvo tai defaultState-parametrin kautta tullut arvo riippuen siitä kumpi löytyy ensin. Funktiokutsujen sisällä olevat or-operaatiot (||) valitsevat aina käytettäväksi arvoksi ensimmäisen, joka voidaan ajatella muuksi kuin tyhjäksi.

Funktion toisena vaiheena luodaan efektitoiminnallisuus useEffect-funktion avulla. Sen avulla voi määritellä toiminnan, joka suoritetaan aina kun jonkin muuttujan arvo päivittyy. Funktiolle annetaan kutsussa kaksi parametria: funktio ja taulukko. Suoritetta toiminta määritellään funktion sisällö ja seurattava(t) muuttuja(t) määritellään taulukossa.

Tässä ohjelmakoodissa useEffect-toiminnan seurattavaksi muuttujaksi määritellään value-muuttuja. Aina, kun seurattavan muuttujan arvo muuttuu, suoritetaan funktion sisällä oleva ohjelmakoodi, joka tallentaa value-arvon localStorage-taltioon.

Funktion kolmantena vaiheena esitellään resetValue-funktio, jonka avulla tallennetun arvon voi palauttaa alkutilanteeseen.

Aivan lopuksi funktio palauttaa taulukossa kolme asiaa:

  • tilamuuttujan,
  • funktion, jolla tilamuuttujan arvo päivitetään ja
  • funktion, jolla tilamuuttujan arvo palautetaan alkuarvoon.

Tiivistetysti voidaan todeta, että edellä esitelty funktio, toimii pääpiirteissään samalla tavalla kuin useState-funktio. Keskeinen ero on se, että tekemämme funktio osaa säilyttää tiedon sivulatausten välillä ja sen arvo voidaan tarvittaessa palauttaa alkuarvoon.

useLocalStorage-funktion hyödyntäminen

Otetaan seuraavaksi edellä toteuttamamme useLocalStorage-funktio käyttöön. Muokkaa src-kansiossa olevaa App.jsx-tiedostoa seuraavasti.

  • Lisää tiedoston alkuun seuraava import-rivi:

    import useLocalStorage from './utils/useLocalStorage';
    
  • Muuta App-funktion alussa olevat olevat useState-alustukset seuraavanlaisiksi:

      // Luodaan taltio, johon tallennetaan pelin laskennalliset tiedot.
      const [stats, setStats, resetStats] = useLocalStorage('lemon-stats',initialstats);
    
      // Luodaan taltio, johon tallennetaan tuotelista.
      const [storeitems,setStoreitems, resetStoreitems] = useLocalStorage('lemon-items',items);
    

    useLocalStorage-funktion toiminta on käytännössä hyvin samanlainen, kuin useState-funktion toiminta. Alustuksessa määritellään key-arvo, jolla muuttujan tiedot tallennetaan localStorage-taltioon.

    Testaus

    Kokeile pelin toimintaa pelaamalla ja välillä uudelleenlataamalla sivun selaimessa.

    Sovellus muistaa nyt pelin tilanteen sivulatausten välillä. Myös pelin tietojen nollaus toimii mainiosti, mutta käytetään siihen useLocalStorage-funktion palauttamia reset-funktiota.

  • Muuta handleReset-funktio seuraavanlaiseksi:

      const handleReset = () => {
        // Palautetaan taltiot alkuarvoihin.
        resetStats();
        resetStoreitems();
      }
    

Nyt sovellus toimii kokonaisuudessaan suunnitelman mukaisesti. Seuraavaksi paketoimme sovelluksen julkaisukuntoon ja julkaisemme sen.

Muutosten vienti versiohallintaan

Viedään viimeisimmät muutokset versiohallintaan.

git add .
git commit -m "lisää tallennuksen localStorage-taltioon"

lisää tallennuksen localStorage-taltioon -commit