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 rukujette 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 form input-e 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('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> ); }
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 render-uje. 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 narudžbine od gostiju i donosi im njihova jela. Ovaj proces naručivanja i posluživanja UI-a ima tri koraka:
- Pokretanje render-a (prenos narudžbine gosta u kuhinju)
- Render-ovanje component-e (priprema narudžbine u kuhinji)
- Commit-ovanje u DOM (postavljanje narudžbine na sto)
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ć umesto toga pokreće re-render. Ovo može biti iznenađujuće na početku!
console.log(count); // 0
setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 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?