Reagointi syötteeseen tilalla
React tarjoaa deklaratiivisen tavan käsitellä käyttöliittymää. Sen sijaan, että manipuloisit suoraan käyttöliittymän yksittäisiä osia, määrittelet eri tilat, joissa komponenttisi voi olla, ja vaihdat niiden välillä käyttäjän syötteen perusteella. Tämä muistuttaa sitä, miten suunnittelijat ajattelevat käyttöliittymästä.
Tulet oppimaan
- Miten deklaratiiviinen käyttöliittymäohjelmointi eroaa imperatiivisesta käyttöliittymäohjelmoinnista
- Kuinka luetella eri visuaaliset tilat, joissa komponenttisi voi olla?
- Miten käynnistää muutokset eri visuaalisten tilojen välillä koodista käsin?
Miten deklaratiivinen käyttöliittymä vertautuu imperatiiviseen
Kun suunnittelet käyttöliittymän vuorovaikutusta, luultavasti mietit, miten käyttöliittymä muuttuu käyttäjän toimien seurauksena. Ajattele lomaketta, jonka avulla käyttäjä voi lähettää vastauksen:
- Kun kirjoitat jotain lomakkeeseen, “Lähetä” painike tulee käyttöön.
- Kun painat “Lähetä”, sekä lomake että painike poistuu käytöstä, ja latausikoni tulee näkyviin.
- Jos verkkopyyntö onnistuu, lomake piiloutuu, ja “Kiitos” viesti tulee näkyviin.
- Jos verkkopyyntö epäonnistuu, virheviesti tulee näkyviin, ja lomake tulee käyttöön uudelleen.
Imperatiivisessa ohjelmoinnissa, edellä mainittu viittaa suoraan siihen miten vuorovaikutus toteutetaan. Sinun täytyy kirjoittaa tarkat ohjeet käyttöliittymän manipulointiin sen perusteella mitä juuri tapahtui. Toinen tapa ajatella tätä on: Kuvittele, että olet autossa jonkun vieressä ja kerrot hänelle mihin käännytään joka käännökseltä.
Illustrated by Rachel Lee Nabors
Hän ei tiedä mihin haluat mennä, noudattavat vain käskyjäsi. (Ja jos annat vääriä ohjeita, päädyt väärään paikkaan!) Tätä kutsutaan imperatiiviseksi, koska jokaista elementtiä täytyy “käskeä”, latausikonista painikkeeseen, kertoen tietokoneelle miten päivittää käyttöliittymää.
Tässä imperatiivisen käyttöliittymäohjelmoinnin esimerkissä lomake on rakennettu ilman Reactia. Se käyttää vain selaimen sisäänrakennettua DOM:a.
async function handleFormSubmit(e) { e.preventDefault(); disable(textarea); disable(button); show(loadingMessage); hide(errorMessage); try { await submitForm(textarea.value); show(successMessage); hide(form); } catch (err) { show(errorMessage); errorMessage.textContent = err.message; } finally { hide(loadingMessage); enable(textarea); enable(button); } } function handleTextareaChange() { if (textarea.value.length === 0) { disable(button); } else { enable(button); } } function hide(el) { el.style.display = 'none'; } function show(el) { el.style.display = ''; } function enable(el) { el.disabled = false; } function disable(el) { el.disabled = true; } function submitForm(answer) { // Oletetaan, että se yhdistäisi verkkoon. return new Promise((resolve, reject) => { setTimeout(() => { if (answer.toLowerCase() == 'istanbul') { resolve(); } else { reject(new Error('Good guess but a wrong answer. Try again!')); } }, 1500); }); } let form = document.getElementById('form'); let textarea = document.getElementById('textarea'); let button = document.getElementById('button'); let loadingMessage = document.getElementById('loading'); let errorMessage = document.getElementById('error'); let successMessage = document.getElementById('success'); form.onsubmit = handleFormSubmit; textarea.oninput = handleTextareaChange;
Käyttöliittymän manipulointi imperatiivisesti toimii hyvin eristetyissä esimerkeissä, mutta siitä tulee eksponentiaalisesti hankalempaa hallita monimutkaisissa järjestelmissä. Kuvittele, että päivität sivua täynnä erilaisia lomakkeita kuten tämä. Uuden käyttöliittymäelementin tai vuorovaikutuksen lisääminen vaatisi huolellista koodin tarkistusta, ettet ole luonut bugia (esimerkiksi, unohtanut näyttää tai piilottaa jotain).
React on rakennettu ratkaisemaan tämä ongelma.
Reactissa et suoraan manipuloi käyttöliittymää—tarkoittaen, että et ota käyttöön, poista käytöstä, näytä/piilota komponentteja suoraan. Sen sijaan määrittelet mitä haluat näyttää, ja React päättelee miten käyttöliittymä päivitetään. Kuvittele olevasi taksissa ja kerrot kuskille mihin haluat mennä sen sijaan, että kertoisit mihin kääntyä. On kuskin tehtävä viedä sinut sinne ja hän saattaa jopa tietää joitain oikoteita, joita et ole saattanut ottaa huomioon!
Illustrated by Rachel Lee Nabors
Käyttöliittymän ajatteleminen deklaratiivisesti
Nyt olet nähnyt ylhäällä miten lomake toteutetaan imperatiivisesti. Jotta ymmärtäisit paremmin, miten Reactissa ajatellaan, käydään alla läpi tämän käyttöliittymän uudelleen toteuttaminen Reactissa:
- Tunnista komponenttisi eri visuaaliset tilat
- Määritä mikä käynnistää nämä tilamuutokset
- Edusta tila muistissa käyttäen
useState
hookkia - Poista kaikki epäolennaiset tilamuuttujat
- Yhdistä Tapahtumankäsittelijät tilan asettamiseksi
1. Vaihe: Tunnista komponenttisi eri visuaaliset tilat
Tietojenkäsittelytieteessä olet saattanut kuulla “tilakoneesta”, joka voi olla yhdessä useista “tiloista”. Jos työskentelet suunnittelijan kanssa, olet saattanut nähdä mallinnuksia erilaisista “visuaalisista tiloista”. React on suunnittelun ja tietotekniikan risteyskohta, joten molemmat ideat ovat inspiraation lähteitä.
Ensiksi, täytyy visualisoida kaikki käyttöliittymän eri “tilat”, joita käyttäjä saattaa nähdä:
- Tyhjä: Lomakkeen “Lähetä” painike on poissa käytöstä.
- Kirjoittaa: Lomakkeen “Lähetä” painike on käytössä.
- Lähettää: Lomake on kokonaan poissa käytöstä. Latausikoni näkyy.
- Onnistui: “Kiitos” viesti näkyy lomakkeen sijaan.
- Virhe: Sama kuin Kirjoittaa -tila, mutta lisävirheviestillä.
Juuri kuten suunnittelija haluat “mallintaa” tai luoda “malleja” eri tiloihin ennen kuin lisäät logiikkaa. Esimerkiksi, tässä on malli vain lomakkeen visuaaliselle osalle. Tämä malli ohjataan status
propsin kautta, jonka oletusarvona on 'empty'
:
export default function Form({ status = 'empty' }) { if (status === 'success') { return <h1>That's right!</h1> } return ( <> <h2>City quiz</h2> <p> In which city is there a billboard that turns air into drinkable water? </p> <form> <textarea /> <br /> <button> Submit </button> </form> </> ) }
Voisit nimetä propsin miten haluat, nimi ei nyt ole niin tärkeää. Kokeile muokata propsi status = 'empty'
arvoon status = 'success'
nähdäksesi onnistumisviestin. Mallintamisen avulla voit nopeasti iteroida käyttöliittymää ennen kuin lisäät logiikkaa. Tässä on täyteläisempi prototyyppi samasta komponentista, joka silti ohjataan status
propsilla:
export default function Form({ // Try 'submitting', 'error', 'success': status = 'empty' }) { if (status === 'success') { return <h1>That's right!</h1> } return ( <> <h2>City quiz</h2> <p> In which city is there a billboard that turns air into drinkable water? </p> <form> <textarea disabled={ status === 'submitting' } /> <br /> <button disabled={ status === 'empty' || status === 'submitting' }> Submit </button> {status === 'error' && <p className="Error"> Good guess but a wrong answer. Try again! </p> } </form> </> ); }
Syväsukellus
Jos komponentilla on monia visuaalisia tiloja, voi olla kätevää näyttää ne kaikki samalla sivulla:
import Form from './Form.js'; let statuses = [ 'empty', 'typing', 'submitting', 'success', 'error', ]; export default function App() { return ( <> {statuses.map(status => ( <section key={status}> <h4>Form ({status}):</h4> <Form status={status} /> </section> ))} </> ); }
Tämänkaltaisia sivuja usein kutsutaan “eläviksi tyyliohjeiksi” tai “storybookeiksi”.
2. Vaihe: Määritä, mikä laukaisee nämä tilamuutokset
Voit käynnistää tilamuutoksen vastauksena kahdenlaiseen syötteeseen:
- Ihmisen syötteeseen, kuten painikeen klikkaaminen, tekstin kirjoittaminen, linkkiin navigoiminen.
- Tietokoneen syötteeseen, kuten verkkopyynnön vastauksen saapuminen, aikakatkaisun päättyminen, kuvan lataaminen.
Illustrated by Rachel Lee Nabors
Molemmissa tapauksissa, saatat asettaa tilamuuttujia käyttöliittymän päivittämiseksi. Kehittämässäsi lomakkeessa sinun on vaihdettava tilaa muutaman eri syötteen perusteella:
- Tekstinsyötön muuttaminen (ihminen) tulisi muuttaa Tyhjä tila Kirjoitetaan -tilaan tai päin vastoin, riippuen siitä onko syöttökenttä tyhjä vai ei.
- Lähetä -painikkeen klikkaaminen (ihminen) tulisi muuttaa tila Lähetetään arvoon.
- Onnistunut verkkovastaus (tietokone) tulisi muuttaa tila Onnistunut arvoon.
- Epäonnistunut verkkovastaus (tietokone) tulisi muuttaa tila Virhe arvoon itse virheviestin kanssa.
Ymmärtääksesi tämän prosessin paremmin voit kokeilla piirtää paperille jokaisen tilan ympyräksi ja jokaisen tilamuutoksen nuolina. Voit hahmotella monia prosesseja tällä tavoin ja selvittää virheet kauan ennen toteutusta.
3. Vaihe: Esitä tila muistissa käyttämällä useState
:a
Seuraavaksi sinun täytyy esitellä komponenttisi visuaaliset tilat muistissa useState
. hookilla. Yksinkertaisuus on avainasemassa: jokainen osa tilaa on “liikkuva osa”, ja haluat niin vähän “liikkuvia osia” kuin mahdollista. Suurempi monimutkaisuus johtaa useampiin virheisiin!
Aloita tilalla, jonka on ehdottomasti oltava siellä. Sinun on esimerkiksi tallennettava answer
syötettä varten ja error
(jos se on olemassa) viimeisimmän virheen tallentamiseksi:
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
Sitten tarvitset tilamuuttujan, joka kuvaa, minkä aiemmin kuvatuista visuaalisista tiloista haluat näyttää. Muistissa on yleensä useampi kuin yksi tapa esittää tämä, joten sinun täytyy kokeilla sitä.
const [isEmpty, setIsEmpty] = useState(true);
const [isTyping, setIsTyping] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const [isError, setIsError] = useState(false);
Ensimmäinen ideasi ei todennäköisesti ole paras mahdollinen, mutta se ei haittaa - tilan muokkaaminen on osa prosessia!
4. Vaihe: Poista kaikki epäolennaiset tilamuuttujat
Haluat välttää toistoa tilasisällössä, jotta seuraat vain olennaisia asioita. Jos käytät hieman aikaa tilarakenteesi uudistamiseen, komponenttisi ovat helpommin ymmärrettävissä, toistoa vähennetään ja tahattomia merkityksiä vältetään. Tavoitteenasi on estää tapaukset, joissa muistissa oleva tila ei edusta mitään pätevää käyttöliittymää, jonka haluaisit käyttäjän näkevän. (Et esimerkiksi koskaan halua näyttää virheilmoitusta ja poistaa syötettä käytöstä samaan aikaan, tai käyttäjä ei pysty korjaamaan virhettä!).
Tässä on joitakin kysymyksiä, joita voit kysyä tilamuuttujiltasi:
- Aiheuttaako tämä tila paradoksin? Esimerkiksi,
isTyping
jaisSubmitting
eivät voi molemmat olla arvoltaantrue
. Paradoksi tarkoittaa yleensä sitä, että tilaa ei ole tarpeeksi rajattu. Kahden totuusarvon yhdistelmiä voi olla neljä, mutta vain kolme vastaa kelvollisia tiloja. Jos haluat poistaa “mahdottoman” tilan, voit yhdistää nämä arvot “tilaksi”, jonka on oltava yksi kolmesta arvosta:'typing'
,'submitting'
, tai'success'
. - Ovatko samat tiedot jo saatavilla toisessa tilamuuttujassa? Toinen paradoksi:
isEmpty
jaisTyping
eivät voi olla arvoltaantrue
samaan aikaan. Tekemällä niistä erilliset tilamuuttujat, vaarana on, että ne menevät sekaisin ja aiheuttavat virheitä. Onneksi voit poistaaisEmpty
ja sen sijaan tarkistaaanswer.length === 0
. - Voiko saman tiedon saada toisen tilamuuttujan käänteisluvusta?
isError
:ia ei tarvita, sillä voit sen sijaan tarkistaaerror !== null
.
Tämän siivouksen jälkeen jäljelle jää 3 (7:stä!) välttämätöntä tilamuuttujaa:
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing'); // 'typing', 'submitting', tai 'success'
Tiedät, että ne ovat välttämättömiä, kun et voi poistaa yhtään niistä rikkomatta toiminnallisuutta.
Syväsukellus
Nämä kolme muuttujaa ovat tarpeeksi kuvaamaan tämän lomakkeen tilaa. Kuitenkin, on jotain välitiloja, jotka eivät ole järkeviä. Esimerkiksi, ei-null error
ei ole järkevä kun status
on success
. Tilan tarkemmaksi mallintamiseksi, voit käyttää reduktoria. Reduktorin avulla voit yhdistää useita tilamuuttujia yhdeksi olioksi ja tiivistää liittyvät logiikat yhteen!
5. Vaihe: Yhdistä Tapahtumankäsittelijät tilan asettamiseen
Lopuksi, luo Tapahtumankäsittelijät, jotka asettavat tilamuuttujat. Alla on lopullinen lomake, jossa kaikki Tapahtumankäsittelijät on kytketty:
import { useState } from 'react'; export default function Form() { const [answer, setAnswer] = useState(''); const [error, setError] = useState(null); const [status, setStatus] = useState('typing'); if (status === 'success') { return <h1>That's right!</h1> } async function handleSubmit(e) { e.preventDefault(); setStatus('submitting'); try { await submitForm(answer); setStatus('success'); } catch (err) { setStatus('typing'); setError(err); } } function handleTextareaChange(e) { setAnswer(e.target.value); } return ( <> <h2>City quiz</h2> <p> In which city is there a billboard that turns air into drinkable water? </p> <form onSubmit={handleSubmit}> <textarea value={answer} onChange={handleTextareaChange} disabled={status === 'submitting'} /> <br /> <button disabled={ answer.length === 0 || status === 'submitting' }> Submit </button> {error !== null && <p className="Error"> {error.message} </p> } </form> </> ); } function submitForm(answer) { // Pretend it's hitting the network. return new Promise((resolve, reject) => { setTimeout(() => { let shouldError = answer.toLowerCase() !== 'lima' if (shouldError) { reject(new Error('Good guess but a wrong answer. Try again!')); } else { resolve(); } }, 1500); }); }
Vaikka tämä koodi ei ole enää alkuperäinen imperatiivinen esimerkki, se on kestävempi. Kaikkien vuorovaikutuksien ilmaiseminen tilamuutoksina antaa sinun ottaa käyttöön uusia visuaalisia tiloja rikkomatta olemassa olevia tiloja. Se myös antaa sinun muuttaa mitä tulisi näyttää eri tiloissa muuttamatta toimintalogiikkaa itsessään.
Kertaus
- Deklaratiivinen ohjelmointi tarkoittaa käyttöliittymän kuvaamista jokaiselle visuaaliselle tilalle toisin kuin käyttöliittymän mikromanagerointi (imperatiivinen).
- Komponenttia kehitettäessä:
- Tunnista kaikki sen visuaaliset tilat.
- Määritä ihmisen ja tietokoneen aiheuttamat tilamuutokset.
- Mallinna tila
useState
:lla. - Poista epäolennainen tila välttääksesi bugeja ja paradokseja.
- Yhdistä Tapahtumankäsittelijät tilan asettamiseen.
Haaste 1 / 3: Lisää ja poista CSS luokka
Toteuta tämä siten, että kuvan klikkaaminen poistaa background--active
CSS luokan sitä ympäröivästä <div>
, mutta lisää picture--active
luokan <img>
elementtiin. Taustan klikkaaminen uudestaan palauttaa alkuperäiset luokat.
Visuaalisesti tulisi odottaa, että klikkaaminen poistaa violetin taustan ja korostaa kuvan reunoja. Kuvan ulkopuolelta klikkaaminen korostaa kuvan taustaa, mutta poistaa kuvan reunojen korostuksen.
export default function Picture() { return ( <div className="background background--active"> <img className="picture" alt="Rainbow houses in Kampung Pelangi, Indonesia" src="https://i.imgur.com/5qwVYb1.jpeg" /> </div> ); }