Interaktiivisuuden lisääminen
Jotkin asiat ruudulla päivittyvät käyttäjän syötteen mukaan. Esimerkiksi, kuvan klikkaaminen galleriassa vaihtaa aktiivista kuvaa. Reactissa ajan myötä muuttuvia tietoja kutsutaan tilaksi. Voit lisätä tilan mihin tahansa komponenttiin ja päivittää sitä tarvittaessa. Tässä luvussa opit miten kirjoitetaan komponentteja, jotka vastaavat toimintohin, päivittävät niiden tilaa sekä näyttävät eri tulostetta ajan myötä.
Tässä kappaleessa
- Miten käsitellä käyttäjä-aloitteisia tapahtumia
- Kuinka saada komponentit “muistamaan” tiedot tilaa hyödyntämällä
- Miten React päivittää käyttöliittymää kahdessa vaiheessa
- Miksi tila ei päivity heti kun muutat sitä
- Miten tilapäivityksiä voi lisätä jonoon
- Miten päivittää oliota tilassa
- Miten päivittää listaa tilassa
Vastaaminen tapahtumiin
Reactissa voit lisätä Tapahtumankäsittelijöitä JSX koodiin. Tapahtumankäsittelijät ovat funktioitasi, joita kutsutaan vastauksena käyttäjän toimintoihin kuten klikkaukseen, hoverointiin, focusointiin ja niin edelleen.
Sisäänrakennetut komponentit kuten <button>
tukevat ainoastaan selaimen sisäänrakennettuja tapahtumia kuten onClick
. Voit kuitenkin luoda omia komponentteja ja niiden Tapahtumankäsittelijöiden nimet voivat olla niin sovelluskohtaisia kuin haluat.
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Playing!')} onUploadImage={() => alert('Uploading!')} /> ); } function Toolbar({onPlayMovie, onUploadImage}) { return ( <div> <Button onClick={onPlayMovie}>Play Movie</Button> <Button onClick={onUploadImage}>Upload Image</Button> </div> ); } function Button({onClick, children}) { return <button onClick={onClick}>{children}</button>; }
Oletko valmis oppimaan tämän aiheen?
Lue Responding to Events oppiaksesi miten lisätä Tapahtumankäsittelijöitä.
Lue lisääTila: komponentin muisti
Komponenttien on usein muutettava näytön sisältöä vuorovaikutuksen seurauksena. Lomakkeeseen kirjoittaminen päivittää syöttökenttää, kuvakarusellissa “seuraava” napsauttaminen tulisi muuttaa näytettävää kuvaa, “osta”-painike asettaa tuotteen ostoskoriin. Komponenttien täytyy “muistaa” asioita: nykyinen syöte, nykyinen kuva, ostoskori. Reactissa tämän kaltaista komponenttikohtaista muistia kutsutaan tilaksi.
Voit lisätä komponentteihin tilan käyttämällä useState
koukkua eli hookkia. Hookit ovat erikoisfunktioita, joilla voit käyttää Reactin ominaisuuksia komponenteissasi (tila on yksi näistä ominaisuuksista). useState
hookilla voit määritellä tilamuuttujan. Sille voidaan antaa alustava tila ja se palauttaa parin arvoja: nykyisen tilan, sekä setter funktion, jolla voit päivittää sitä.
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
Tässä näet miten kuvagalleria käyttää käyttää ja päivittää tilaa napsautuksella:
import {useState} from 'react'; import {sculptureList} from './data.js'; export default function Gallery() { const [index, setIndex] = useState(0); const [showMore, setShowMore] = useState(false); const hasNext = index < sculptureList.length - 1; function handleNextClick() { if (hasNext) { setIndex(index + 1); } else { setIndex(0); } } function handleMoreClick() { setShowMore(!showMore); } let sculpture = sculptureList[index]; return ( <> <button onClick={handleNextClick}>Next</button> <h2> <i>{sculpture.name} </i> by {sculpture.artist} </h2> <h3> ({index + 1} of {sculptureList.length}) </h3> <button onClick={handleMoreClick}> {showMore ? 'Hide' : 'Show'} details </button> {showMore && <p>{sculpture.description}</p>} <img src={sculpture.url} alt={sculpture.alt} /> </> ); }
Oletko valmis oppimaan tämän aiheen?
Lue Tila: Komponentin muisti oppiaksesi miten arvo muistetaan ja päivitetään vuorovaikutuksen vuoksi.
Lue lisääRenderöinti ja kommitointi
Ennen kuin komponenttisi näytetään ruudlla, Reactin täytyy renderöidä ne. Tämän prosessin vaiheiden ymmärtäminen auttaa sinua miettimään miten koodiasi suoritetaan ja selittämään sen toimintaa.
Kuvittele, että komponettisi ovat kokkeja keittiössä, kasaten maukkaita aterioita ainesosista. Tässä skenaariossa React on tarjoilija joka laittaa ylös asiakkaiden tilaukset sekä vie tilaukset heille. Tässä käyttöliittymän pyyntö- ja vientiprosessissa on kolme vaihetta:
- Käynnistetään renderöinti (viedään ruokalijan tilaus keittiölle)
- Renderöidään komponentti (valmistellaan tilausta keittiössä)
- Kommitoidaan DOM:iin (asetetaan tilaus pöydälle)
Illustrated by Rachel Lee Nabors
Oletko valmis oppimaan tämän aiheen?
Lue Render and Commit oppiaksesi käyttöliittymäpäivityksen elämänkaaren.
Lue lisääTila tilannekuvana
Toisin kuin tavalliset JavaScript muuttujat, Reactin tila käyttäytyy enemmän kuin tilannekuva. Tilan muuttaminen ei muuta tilamuuttujaa joka sinulla jo on, vaan sen sijaan käynnistää uudelleenrenderöinnin. Tämä saattaa olla yllättävää aluksi!
console.log(count); // 0
setCount(count + 1); // Pyydä uudelleenrenderöinti arvolla 1
console.log(count); // Silti 0!
Tämä käyttäytyminen helpottaa välttämään hienovaraisia bugeja. Tässä on pieni chätti-sovellus. Veikkaa mitä tapahtuu kun painat ensin “Lähetä” ja sitten muutat vastaanottajan Jaakoksi. Kenen nimi tulee näkymään alert
:ssa viiden sekuntin jälkeen?
import {useState} from 'react'; export default function Form() { const [to, setTo] = useState('Liisa'); const [message, setMessage] = useState('Hei'); function handleSubmit(e) { e.preventDefault(); setTimeout(() => { alert(`Sanoit ${message} henkilölle ${to}`); }, 5000); } return ( <form onSubmit={handleSubmit}> <label> Henkilölle:{' '} <select value={to} onChange={(e) => setTo(e.target.value)}> <option value="Liisa">Liisa</option> <option value="Jaakko">Jaakko</option> </select> </label> <textarea placeholder="Viesti" value={message} onChange={(e) => setMessage(e.target.value)} /> <button type="submit">Lähetä</button> </form> ); }
Oletko valmis oppimaan tämän aiheen?
Lue Tila tilannekuvana oppiaksesi miksi tila vaikuttaa “vakiolta” ja muuttumattomalta Tapahtumankäsittelijöissä.
Lue lisääTilapäivityksen lisääminen jonoon
Tämä komponentti on buginen: kun klikataan “+3” se nostaa pisteytystä vain kerran.
import {useState} from 'react'; export default function Counter() { const [score, setScore] = useState(0); function increment() { setScore(score + 1); } return ( <> <button onClick={() => increment()}>+1</button> <button onClick={() => { increment(); increment(); increment(); }}> +3 </button> <h1>Score: {score}</h1> </> ); }
Tila tilannekuvana selittää miksi näin tapahtuu. Tilan asettaminen pyytää uudelleenrenderöinnin, mutta ei muuta sitä jo suoritettavassa koodissa. Joten score
jää arvoksi 0
heti sen jälkeen kun kutsut setScore(score + 1)
.
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
Voit korjata tämän välittämällä päivitysfunktion tilamuutoksen yhteydessä. Huomaa miten setScore(score + 1)
muuttaminen setScore(s => s + 1)
funktioksi korjaa “+3” painikkeen. Tämän avulla voit lisätä useita tilamuutoksia jonoon.
import {useState} from 'react'; export default function Counter() { const [score, setScore] = useState(0); function increment() { setScore((s) => s + 1); } return ( <> <button onClick={() => increment()}>+1</button> <button onClick={() => { increment(); increment(); increment(); }}> +3 </button> <h1>Score: {score}</h1> </> ); }
Oletko valmis oppimaan tämän aiheen?
Lue Tilapäivityksen lisääminen jonoon oppiaksesi miten lisätään useita tilapäivityksiä jonoon.
Lue lisääOlioiden päivittäminen tilassa
Tila voi pitää sisällään mitä vain JavaScript arvoa, kuten olioita. Mutta React tilassa olevia olioita ja listoja ei pitäisi muuttaa suoraan. Sen sijaan kun haluat päivittää oliota ja listaa, luo uusi (tai tee kopio olemassa olevasta), ja sitten päivitä tila käyttämään uutta kopiota.
Yleensä käytät ...
levityssyntaksia kopioidaksesi oliot ja listat joita haluat muuttaa. Esimerkiksi, sisäkkkäisen olion päivittäminen voisi näyttää seuraavalta:
import {useState} from 'react'; export default function Form() { const [person, setPerson] = useState({ name: 'Niki de Saint Phalle', artwork: { title: 'Blue Nana', city: 'Hamburg', image: 'https://i.imgur.com/Sd1AgUOm.jpg', }, }); function handleNameChange(e) { setPerson({ ...person, name: e.target.value, }); } function handleTitleChange(e) { setPerson({ ...person, artwork: { ...person.artwork, title: e.target.value, }, }); } function handleCityChange(e) { setPerson({ ...person, artwork: { ...person.artwork, city: e.target.value, }, }); } function handleImageChange(e) { setPerson({ ...person, artwork: { ...person.artwork, image: e.target.value, }, }); } return ( <> <label> Name: <input value={person.name} onChange={handleNameChange} /> </label> <label> Title: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> City: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Image: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {' by '} {person.name} <br /> (located in {person.artwork.city}) </p> <img src={person.artwork.image} alt={person.artwork.title} /> </> ); }
Jos olioiden kopiointi käy tylsäksi, voit käyttää kirjastoa kuten Immer vähentääksesi toistettavaa koodia:
import {useImmer} from 'use-immer'; export default function Form() { const [person, updatePerson] = useImmer({ name: 'Niki de Saint Phalle', artwork: { title: 'Blue Nana', city: 'Hamburg', image: 'https://i.imgur.com/Sd1AgUOm.jpg', }, }); function handleNameChange(e) { updatePerson((draft) => { draft.name = e.target.value; }); } function handleTitleChange(e) { updatePerson((draft) => { draft.artwork.title = e.target.value; }); } function handleCityChange(e) { updatePerson((draft) => { draft.artwork.city = e.target.value; }); } function handleImageChange(e) { updatePerson((draft) => { draft.artwork.image = e.target.value; }); } return ( <> <label> Name: <input value={person.name} onChange={handleNameChange} /> </label> <label> Title: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> City: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Image: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {' by '} {person.name} <br /> (located in {person.artwork.city}) </p> <img src={person.artwork.image} alt={person.artwork.title} /> </> ); }
Oletko valmis oppimaan tämän aiheen?
Lue Updating Objects in State oppiaksesi miten olioita voidaan päivittää oikeaoppisesti.
Lue lisääListojen päivittäminen tilassa
Listat ovat toisen tyyppisiä muuttuvia JavaScript olioita, joita voit tallettaa tilaan, ja joita tulisi käsitellä vain-luku muotoisina. Juuri kuten olioiden kanssa, kun haluat päivittää listaa tilassa, tarvitset uuden listan (tai kopion olemassa olevasta), ja sitten aseta tila käyttämään uutta listaa:
import {useState} from 'react'; let nextId = 3; const initialList = [ {id: 0, title: 'Big Bellies', seen: false}, {id: 1, title: 'Lunar Landscape', seen: false}, {id: 2, title: 'Terracotta Army', seen: true}, ]; export default function BucketList() { const [list, setList] = useState(initialList); function handleToggle(artworkId, nextSeen) { setList( list.map((artwork) => { if (artwork.id === artworkId) { return {...artwork, seen: nextSeen}; } else { return artwork; } }) ); } return ( <> <h1>Art Bucket List</h1> <h2>My list of art to see:</h2> <ItemList artworks={list} onToggle={handleToggle} /> </> ); } function ItemList({artworks, onToggle}) { return ( <ul> {artworks.map((artwork) => ( <li key={artwork.id}> <label> <input type="checkbox" checked={artwork.seen} onChange={(e) => { onToggle(artwork.id, e.target.checked); }} /> {artwork.title} </label> </li> ))} </ul> ); }
Jos olioiden kopiointi käy tylsäksi, voit käyttää kirjastoa kuten Immer vähentääksesi toistettavaa koodia:
import {useState} from 'react'; import {useImmer} from 'use-immer'; let nextId = 3; const initialList = [ {id: 0, title: 'Big Bellies', seen: false}, {id: 1, title: 'Lunar Landscape', seen: false}, {id: 2, title: 'Terracotta Army', seen: true}, ]; export default function BucketList() { const [list, updateList] = useImmer(initialList); function handleToggle(artworkId, nextSeen) { updateList((draft) => { const artwork = draft.find((a) => a.id === artworkId); artwork.seen = nextSeen; }); } return ( <> <h1>Art Bucket List</h1> <h2>My list of art to see:</h2> <ItemList artworks={list} onToggle={handleToggle} /> </> ); } function ItemList({artworks, onToggle}) { return ( <ul> {artworks.map((artwork) => ( <li key={artwork.id}> <label> <input type="checkbox" checked={artwork.seen} onChange={(e) => { onToggle(artwork.id, e.target.checked); }} /> {artwork.title} </label> </li> ))} </ul> ); }
Oletko valmis oppimaan tämän aiheen?
Lue Updating Arrays in State oppiaksesi miten listoja päivitetään oikeaoppisesti.
Lue lisääMitä seuraavaksi?
Siirry seuraavaksi Vastaaminen tapahtumiin -sivulle lukeaksesi tämän luvun sivu kerrallaan!
Tai, jos aiheet ovat jo tuttuja, mikset lukisi Tilan hallintaa?