React
- JSX
- Hello world
- React-function-komponentit
- react ja silmukat
- Lomakkeet
- React-komponentin tila (setState)
- Lisätietoa
React on Facebookin ja Instagramin Javascript-kirjasto käyttöliittymien tekemiseen.
Tämä sivu on päivitetty käyttämään function-komponentteja. Osa esimerkeistä voi vielä olla myös class-syntaksilla.
- esimerkki function-komponenteilla (2022) ja Esimerkki toteutettuna class-komponenteilla (vanha).
- kontrolloitu lomake-esimerkki (function-versio, 2022), controlled.js
- kontrolloitu lomake-esimerkki (vanha class-versio)
- kontrolloimaton lomake-esimerkki (function-versio, 2022)
- kontrolloimaton lomake-esimerkki (vanha class-versio)
Kannattaa katsoa seuraava video: React Functional Components, Props, and JSX – React.js Tutorial for Beginners
- React päivittää käyttöliittymän automaattisesti kun data muuttuu
- React osaa päivittää käyttöliittymästä vain ne osat jotka liittyvät muuttuneeseen dataan
- Reactilla toteutettu käyttöliittymä muodostuu koostetuista komponenteista. React ei käytä templateja vaan Javascriptia ja JSX:ää
- React vastaa MVC-arkkitehtuurissa näkymää (V)
- Thinking in React
JSX
- JSX on hieman XML-kieleltä näyttävää Javascriptia
- JSX:n avulla voi kirjoittaa XML-tyylistä koodia jossa on sekaisin html-elementtejä ja React-komponentteja.
Html-elementit kirjoitetaan pienillä kirjaimilla. React-komponenttien nimet aloitetaan kapiteeleilla.
<h1>Otsikko</h1> <MyComponent /> <MyList items={[1,2,3]}/>
- JSX transformoidaan tavalliseksi Javascript-koodiksi. Transformoinnin voi tehdä selaimella lennosta mutta tuotantokäytössä suositellaan, että koodi käännetään javascriptiksi etukäteen.
- Elementin attribuuteissa sijaitseva Javascript-koodi sijoitetaan JSX-syntaksissa {}-merkkien sisään."-merkkejä ei käytetä.
- JSX in Depth
- React toimii paikoin hieman eri tavalla kuin tavallinen DOM-rajapinta ja HTML-elementit. Lue: DOM-elements
Hello world
Yksinkertaisimmassa React-ohjelmassa liitetään html-dokumenttiin react-kirjasto, JSXTransformer-kirjasto ja kirjoitetaan script-elementin sisään oma yksinkertainen komponentti funktion muodossa ja renderoidaan se. Huom. script-elementin type-attribuutin arvon on oltava text/jsx.
kts. Quick Start ja tutorial
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
<div id="main"></div>
<script type="text/jsx">
// yksinkertainen komponentti, joka palauttaa tekstikappaleen jossa lukee haluttu nimi
// komponentille annetut parametrit löytyvät this.props-objektista
const HelloWorld = function(props){
return (
<p>
Hello {props.name}!
</p>
);
}
// näytetään main-tunnisteella varustetun lohkon sisällä edellä luotu oma komponentti
// esitettävä nimi annetaan attribuuttina
const root = ReactDOM.createRoot( document.getElementById('main'));
root.render(
<HelloWorld name="Tommi" />
);
</script>
</body>
</html>
React-function-komponentit
let Komponentti = function(props) {
return ( <p>Hello World!</p> );
}
- Komponentti voi koostua muista komponenteista ja/tai html-elementeistä
- Komponentille annetut parametrit (elementin attribuutit) löytyvät props-parametrista.
props-parametrien sisältöä ei saa itse muuttaa.
let Komponentti = function(props) { return ( <p>Hello {props.nimi}</p> ); }
- Komponentin tilaan voidaan tallentaa mitä tahansa objekteja.
Tilamuuttujan (laskuri) alustuksessa luodaan myös tilamuuttujan muuttamiseen käytettävä
funktio (setLaskuri)
let Laskuri = function(props) { const [laskuri, setLaskuri] = React.useState(0); return ( <p>{laskuri}</p> ); }
- Jos suinkin mahdollista niin älä käytä komponentissasi tilaa. Yritä sijoittaa kaikki sovelluksen tilaan liittyvät muuttujat komponenttihierarkiassa mahdollisimman korkealle tasolle ja vain yhteen paikkaan. kts. Writing good react components
- Components and Props
- Tapahtumankäsittely on yksinkertaista:
let Laskuri = function(props) { const [laskuri, setLaskuri] = React.useState(0); const kasvataLaskuria = function(e) { setLaskuri(laskuri+1); } return ( <p>{laskuri} <input type="text" onChange={kasvataLaskuria} /> </p> ); }
- DOM-rajapinnan kautta html-elementteihin ei yleensä tarvi päästä käsiksi. Lomakkeiden yhteydessä esim. lomakkeen kenttiin pääsee käsiksi tapahtumankäsittelijöiden yhteydessä (e.target). refs and the DOM
- Jos haluatkin käyttää luokkakomponentteja tutustu: React-luokkakomponentit
react ja silmukat
Reactin JSX-koodiin ei voi kirjoittaa kuin React-elementteja, html-elementtejä ja {}-sulkujen sisään funktiokutsuja tai laskutoimituksia ja muuttujien nimiä. Jos on tarve käyttää for-looppia tai muita vastaavia rakenteita on nämä suoritettava erikseen. Sisäkkäisten for-looppien tapauksessa on toinen looppi piilotettava erilliseen funktioon tai komponenttiin
Allaolevan esimerkin vanhaa versiota voi kokeilla pääteohjausmallissa.
let Test = function() {
let taulukko = ["foo", "bar", "foobar", "barfoo"];
let taulukko2 = [1, 2, 3, 4];
let aputaulukko = [];
// JSX-koodissa voi olla vain funktiokutsuja tai muuttujia
// tässä tapauksessa taulukko[i] kelpaa hyvin <li></li>-rakenteen sisään
// muista antaa jokin uniikki avain jokaiselle riville
for(let i=0;i<taulukko.length;i++) {
aputaulukko.push( <li key={i}>{taulukko[i]}</li> );
}
aputaulukko = [];
// entä jos on kaksi for-looppia?
// tulostetaan sisempään listaan kaikki saman taulukon alkiot
// toinen for-looppi on piilotettava toiseen funktioon
// for(let i=0;i<taulukko2.length;i++) {
// aputaulukko.push( apufunktio(taulukko2[i], taulukko) );
// }
// sama kuin edellä mutta käyttäen toista react-komponenttia
for(let i=0;i<taulukko2.length;i++) {
aputaulukko.push( <Apuluokka key={i} taulukko={taulukko} ulompi={taulukko2[i]} /> );
}
return (
<ul>
{ aputaulukko }
</ul>
)
/* tämä ei toimi vaan tulee unexpected token -virheilmoitus
return (
<ul> {
for(let i=0;i<taulukko.length;i++) {
aputaulukko.push( <li>{taulukko[i]}</li> );
}}
</ul>
)
*/
}
// tekee saman asian kuin alempana oleva apufunktio
let Apuluokka = function(props) {
let aputaulukko = [];
for(let i=0;i<props.taulukko.length;i++) {
aputaulukko.push( <li key={i}>{props.taulukko[i]}</li> );
}
return (
<li>
{props.ulompi}
<ul>
{ aputaulukko }
</ul>
</li>
)
}
});
// tekee sisemmän for-loopin, parametrina tuodaan ulomman listan teksti ja sisempään
// listaan vaadittava taulukko
function apufunktio(sarake, taulukko) {
let aputaulukko = [];
for(let i=0;i<taulukko.length;i++) {
aputaulukko.push( <li key={i}>{taulukko[i]}</li> );
}
return (
<li key={sarake}>
{sarake}
<ul>
{ aputaulukko }
</ul>
</li>
)
}
Lomakkeet
Oletuksena Reactissa lomakkeet ovat kontrolloituja komponentteja. Kontrolloidussa lomakkeessa lomakkeen sisältö vastaa aina React-komponentin tilaa. Käytä kontrolloitua lomaketta aina kuin vain mahdollista. Jossain tilanteessa voit tehdä lomakkeesta myös kontrolloimattoman komponentin. Kts. esimerkit:
- kontrolloitu lomake-esimerkki (function-versio, 2022)
- kontrolloitu lomake-esimerkki (vanha class-versio)
- kontrolloimaton lomake-esimerkki (function-versio, 2022)
- kontrolloimaton lomake-esimerkki (vanha class-versio)
Lomakkeissa voi käyttää samoja tarkistuksia ja virheilmoituksia kuin normaalistikin. Kts. validointiesimerkki (function-versio, 2022) tai validointiesimerkki (class-versio, kontrolloimaton)
React-komponentin tila (setState)
Huomioi Reactista seuraavat seikat:
- props sisältää komponentille attribuutteina tuodut parametrit. Näiden arvoja ei voi itse ohjelmakoodissa muuttaa.
- state sisältää komponentin tilaan liittyvät muuttujat. Niitä voi ja pitää muuttaa mutta muuttamisen saa tehdä vain setState-metodilla. Tilamuuttujia voi olla useita ja niiden nimet voivat olla mitä tahansa, mutta tapana on, että muuttujaa vastaava funktio on aina nimeltään setMuuttuja. Jos muutat komponentin tilaa, React päivittää muutokset automaattisesti näkyville sivulle.
- Objektien päivittäminen on helpointa tehdä spread-syntaksin
avulla.
const [objtila, setObjtila] = React.useState({ "foo": "bar", "bar": "foobar", "foobar": "barbar", "taulukko": [1,2,3] }); ... // seuraava ...objtila tekee shallow-kopion objtila-muuttujasta, josta // seuraavalla rivillä asetetaan "foobar"-avaimelle uusi arvo // jos tarvittaisin deep copy, niin taulukko pitäisi vielä erikseen kopioida, // mutta koska vain "foobar" muuttuu, niin shallow-kopio riittää tässä tilanteessa setObjtila( {...objtila, "foobar": "uusi arvo" }
Asynkroninen setState
Huom!. Tilan päivittäminen tapahtuu asynkronisesti. Kts. Why is setState fiving me the wrong value
Asynkronisuus tarkoittaa, että ette voi tietää missä vaiheessa state oikeasti päivittyy. Esim. seuraavanlainen koodi erittäin todennäköisesti tulostaa vielä vanhan tilan eikä sitä mikä on juuri yritetty asettaa:
uusi.joukkueet.push(joukkue);
setState({...state, "joukkueet" : uusi });
console.log(this.state.joukkueet);
Monimutkainen tila
Huom!. Jos tallennat tilaan objekteja tai taulukoita niin ole tarkkana tilaa kopioitaessa. Kts. esimerkki (lähdekoodi). Lisätietoa: How to update nested state properties in React ja Handling State in React: Four Immutable Approaches to Consider.
Jos tarvitset komponentissa muuttujia, jotka eivät vaikuta komponentin renderointiin, niin näitä ei tarvitse käyttää staten kautta vaan voit vapaasti keksiä omia. Esim. let foobar = "omajuttu"
Käyttäjien kommentit