Dodavanje interaktivnosti
Neke stvari na ekranu se update-uju kao odgovor na korisnički unos. Na primer, klik na galeriju slika menja aktivnu sliku. U React-u, podaci koji se menjaju tokom vremena nazivaju se state. Možete dodati state bilo kojoj component-i i update-ovati je po potrebi. U ovom poglavlju naučićete kako da pišete component-e koji obrađuju interakcije, update-uju svoj state i prikazuju različite output-e tokom vremena.
U ovom poglavlju:
- Kako da rukujete event-ima koje pokrene korisnik
- Kako da učinite da component-e „pamte” informacije koristeći state
- Kako React update-uje UI u dve faze
- Zašto se state ne update-uje odmah nakon promene
- Kako da složite seriju state update-ova
- Kako da update-ujete objekat u state-u
- Kako da update-ujete array u state-u
Odgovaranje na event-e
React vam omogućava da dodate event handler-e u vaš JSX. Event handler-i su vaše sopstvene funkcije koje će se pokrenuti kao odgovor na korisničke interakcije poput klika, prelaženja mišem, fokusiranja na input-e forme i tako dalje.
Ugrađene component-e poput <button>
podržavaju samo ugrađene browser event-e poput onClick
. Međutim, možete kreirati i sopstvene component-e i njihovim event handler prop-ovima dati bilo koja aplikacijski specifična imena koja želite.
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Puštanje!')} onUploadImage={() => alert('Upload-ovanje!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Pusti film </Button> <Button onClick={onUploadImage}> Upload-uj sliku </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
Ready to learn this topic?
Pročitajte Odgovaranje na event-e da biste naučili kako da dodate event handler-e.
Read MoreState: Memorija component-e
Component-e često moraju da promene ono što je prikazano na ekranu kao rezultat neke interakcije. Kucanje u formu treba da update-uje polje za unos, klik na “sledeće” u karuselu slika treba da promeni prikazanu sliku, klik na “kupi” stavlja proizvod u korpu. Component-e moraju da “pamte” određene stvari: trenutnu vrednost unosa, trenutnu sliku, korpu za kupovinu. U React-u, ova vrsta memorije specifična za component-e naziva se state.
Možete dodati state component-i pomoću Hook-a useState
. Hook-ovi su specijalne funkcije koje omogućavaju vašim component-ama da koriste React funkcionalnosti (state je jedna od tih funkcionalnosti). Hook useState
vam omogućava da deklarišete varijablu state-a. On prima inicijalni state i vraća par vrednosti: trenutni state i funkciju za setovanje state-a koja vam omogućava da ga update-ujete.
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
Here is how an image gallery uses and updates state on click:
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} /> </> ); }
Ready to learn this topic?
Pročitajte State: Memorija Component-e kako biste naučili kako da zapamtite vrednost i update-ujete je prilikom interakcije.
Read MoreRender i commit
Pre nego što se vaše component-e prikažu na ekranu, React mora da ih renderuje. Razumevanje koraka u ovom procesu pomoći će vam da razmislite o tome kako se vaš kod izvršava i da objasnite njegovo ponašanje.
Zamislite da su vaše component-e kuvari u kuhinji, koji pripremaju ukusna jela od sastojaka. U ovom scenariju, React je konobar koji prenosi porudžbine od gostiju i donosi im njihova jela. Ovaj proces poručivanja i posluživanja UI-a ima tri koraka:
- Pokretanje rendera (prenos porudžbine gosta u kuhinju)
- Renderovanje component-e (priprema porudžbine u kuhinji)
- Commit-ovanje u DOM (postavljanje porudžbine na sto)
Pokretanje Render Commit
Illustrated by Rachel Lee Nabors
Ready to learn this topic?
Pročitajte Render i Commit kako biste naučili lifecycle jednog UI update-a.
Read MoreState kao snapshot
Za razliku od običnih JavaScript varijabli, React state se ponaša više kao snapshot. Njegovo postavljanje ne menja već postojeću state varijablu, već pokreće ponovni render. Ovo može biti iznenađujuće na početku!
console.log(count); // 0
setCount(count + 1); // Zatraži ponovni render sa 1
console.log(count); // I dalje 0!
Ovo ponašanje vam pomaže da izbegnete suptilne greške. Evo male aplikacije za ćaskanje. Pokušajte da pogodite šta će se desiti ako prvo pritisnete “Pošalji”, a zatim promenite primaoca na Bob. Čije ime će se pojaviti u alert
pet sekundi kasnije?
import { useState } from 'react'; export default function Form() { const [to, setTo] = useState('Alice'); const [message, setMessage] = useState('Hello'); function handleSubmit(e) { e.preventDefault(); setTimeout(() => { alert(`Poruka ${message} za ${to}`); }, 5000); } return ( <form onSubmit={handleSubmit}> <label> To:{' '} <select value={to} onChange={e => setTo(e.target.value)}> <option value="Alice">Alice</option> <option value="Bob">Bob</option> </select> </label> <textarea placeholder="Message" value={message} onChange={e => setMessage(e.target.value)} /> <button type="submit">Send</button> </form> ); }
Ready to learn this topic?
Pročitajte State kao snapshot da biste saznali zašto state izgleda “fiksno” i nepromenljivo unutar event handler-a.
Read MoreRedosled serijskih update-ova state-a
Ova component-a ima grešku: klikom na dugme “+3” rezultat se povećava samo jednom.
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> </> ) }
State kao Snapshot objašnjava zašto se ovo dešava. Postavljanje state-a traži novo render-ovanje, ali ne menja njegovu vrednost u kodu koji se već izvršava. Tako score
nastavlja da ima vrednost 0
odmah nakon što pozovete 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
Možete ovo popraviti prosleđivanjem updater funkcije kada postavljate state. Obratite pažnju kako zamena setScore(score + 1)
sa setScore(s => s + 1)
rešava problem dugmeta “+3”. Ovo vam omogućava da redom dodate više update-ova state-a u redosled.
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> </> ) }
Ready to learn this topic?
Pročitajte Redosled serije state update-ova](/learn/queueing-a-series-of-state-updates) kako biste naučili kako da postavite redosled update-ova state-a.
Read MoreUpdate-ovanje objekata u state-u
State može sadržati bilo koju vrstu JavaScript vrednosti, uključujući objekte. Međutim, ne bi trebalo direktno menjati objekte i array-e koje držite u React state-u. Umesto toga, kada želite da update-ujete objekat ili array, potrebno je da kreirate novi (ili napravite kopiju postojećeg) i zatim update-ujete state kako bi koristio tu kopiju.
Obično ćete koristiti ...
spread sintaksu za kopiranje objekata i array-a koje želite da promenite. Na primer, update-ovanje ugnježdenog objekta može izgledati ovako:
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} /> </> ); }
Ako kopiranje objekata u kodu postane zamorno, možete koristiti biblioteku poput Immer kako biste smanjili količinu ponavljajućeg koda:
{ "dependencies": { "immer": "1.7.3", "react": "latest", "react-dom": "latest", "react-scripts": "latest", "use-immer": "0.5.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" }, "devDependencies": {} }
Ready to learn this topic?
Pročitajte Update-ovanje Objekata u State-u kako biste naučili kako pravilno update-ovati objekte.
Read MoreUpdate-ovanje array-a u state-u
Array-i su još jedan tip promenljivih JavaScript objekata koje možete čuvati u state-u i koje treba tretirati kao read-only. Kao i kod objekata, kada želite da update-ujete array koji se nalazi u state-u, potrebno je da kreirate novi (ili napravite kopiju postojećeg), a zatim postavite state da koristi taj novi array:
import { useState } from 'react'; 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> ); }
Ako pravljenje kopija array-a u kodu postane zamorno, možete koristiti biblioteku kao što je Immer da smanjite ponavljanje koda:
{ "dependencies": { "immer": "1.7.3", "react": "latest", "react-dom": "latest", "react-scripts": "latest", "use-immer": "0.5.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" }, "devDependencies": {} }
Ready to learn this topic?
Pročitajte Update-ovanje Array-a u State-u da biste naučili kako pravilno da update-ujete array-e.
Read MoreŠta je sledeće?
Pređite na Reagovanje na Event-e da biste počeli da čitate ovo poglavlje stranicu po stranicu!
Ili, ako ste već upoznati sa ovim temama, zašto ne biste pročitali Upravljanje State-om?