Tapahtumiin vastaaminen

React antaa sinun lisätä tapahtumakäsittelijöitä JSX:sssä. Tapahtumakäsittelijät ovat omia funktioitasi, joita kutsutaan vastauksena vuorovaikutuksiin kuten klikkauseen, hoveriin, lomakkeiden kohteiden focusointiin, jne.

Tulet oppimaan

  • Eri tavat kirjoittaa tapahtumakäsittelijä
  • Miten välittää tapahtumakäsittelijän logiikka pääkomponentista
  • Miten tapahtumat leviävät ja miten sen voi estää

Tapahtumakäsittelijöiden lisääminen

Lisätäksesi tapahtumakäsittelijän, täytyy ensiksi määritellä funktio ja sitten välittää se propsina sopivalle JSX tagille. Esimerkiksi, tässä on painike, joka ei tee vielä mitään:

export default function Button() {
  return (
    <button>
      I don't do anything
    </button>
  );
}

Voit laittaa sen näyttämään viestin kun käyttäjä klikkaa tekemällä nämä kolme vaihetta:

  1. Määritä funktio nimeltään handleClick komponentin Button sisällä.
  2. Toteuta logiikka funktion sisällä (käytä alert funktiota näyttääksesi viestin).
  3. Lisää onClick={handleClick} <button> tagiin JSX:ssä.
export default function Button() {
  function handleClick() {
    alert('You clicked me!');
  }

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

Määrittelit handleClick funktion ja välitit sen propsina <button>:lle. handleClick on tapahtumaksäittelijä. Tapahtumakäsittelijäfunktiot:

  • ovat useimmiten määritelty komponenttien sisällä.
  • alkavat sanalla handle, jonka jälkeen nimeen tulee tapahtuman nimi.

Tavanomaisesti tapahtumakäsittelijät alkavat sanalla handle ja sisältävät tapahtuman nimen. Näet usein onClick={handleClick}, onMouseEnter={handleMouseEnter}, ja niin edelleen.

Vaihtoehtoisesti voit määritellä tapahtumakäsittelijän samalla rivillä JSX:ssä.

<button onClick={function handleClick() {
alert('You clicked me!');
}}>

Tai tiiviimmin nuolifunktioilla:

<button onClick={() => {
alert('You clicked me!');
}}>

Kaikki nämä tyylit vastaavat toisiaan. Samalla rivillä olevat tapahtumakäsittelijät ovat käteviä lyhyihin funktioihin.

Sudenkuoppa

Tapahtumakäsittelijöihin annetut funktiot täytyy välitää, niitä ei pidä kutsua. Esimerkiksi:

funktion välittäminen (oikein)funktion kutsuminen (väärin)
<button onClick={handleClick}><button onClick={handleClick()}>

Ero on hienovarainen. Ensimmäisessä esimerkissä handleClick funktio välitetään onClick tapahtumakäsittelijäksi. Tämä kertoo Reactille, että sen täytyy muistaa se ja kutsua sitä vain kun käyttäjä klikkaa painiketta.

Toisessa esimerkissä handleClick() lopussa oleva () kutsuu funktiota heti renderöinnin aikana ilman yhtään klikkausta. Näin tapahtuu, koska JSX aaltosulkeiden välissä { ja } oleva JavaScript suoritetaan heti.

Kun kirjoitat koodia samalle riville, sama sudenkuoppa esiintyy uudelleen eri tavalla:

funktion välittäminen (oikein)funktion kutsuminen (väärin)
<button onClick={() => alert('...')}><button onClick={alert('...')}>

Tällä tavoin annettua koodia ei kutsuta klikkauksen yhteydessä. Sitä kutsutaan joka kerta kun komponentti renderöidään:

// Tämä ilmoitus kutsutaan kun komponentti renderöidään, ei klikattaessa!
<button onClick={alert('You clicked me!')}>

Jos haluat määritellä tapahtumakäsittelijän samalla rivillä, kääri se anonyymiin funktioon tällä tavoin:

<button onClick={() => alert('You clicked me!')}>

Sen sijaan, että koodi suoritettaisiin joka renderöinnin yhteydessä, tämä luo uuden funktion myöhemmin kutsuttavaksi.

Molemmissa tilanteissa välität funktion:

  • <button onClick={handleClick}> välittää handleClick funktion.
  • <button onClick={() => alert('...')}> välittää () => alert('...') funktion.

Lue lisää nuolifunktioista.

Propsien lukeminen tapahtumakäsittelijöissä

Sillä tapahtumakäsittelijät ovat määritelty komponentin sisällä, niillä on pääsy komponenttien propseihin. Tässä on painike, joka klikattaessa näyttää message prosin ilmoituksena:

function AlertButton({ message, children }) {
  return (
    <button onClick={() => alert(message)}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div>
      <AlertButton message="Playing!">
        Play Movie
      </AlertButton>
      <AlertButton message="Uploading!">
        Upload Image
      </AlertButton>
    </div>
  );
}

Näin nämä kaksi painiketta voivat näyttää eri viestejä. Kokeile muuttaa niille välitettyjä viestejä.

Tapahtumakäsittelijöiden välittäminen propseina

Usein haluat pääkomponentin pystyä määritellä alakomponentin tapahtumakäsittelijän. Esimerkiksi painikkeet: riippuen missä käytät Button komponenttia, saatat haluta kutsua eri funktiota. Ehkäpä yksi toistaa videota ja toinen lähettää kuvan palvelimelle.

Voit tehdä tämän välittämällä tapahtumakäsittelijän propsina alakomponentille:

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

function PlayButton({ movieName }) {
  function handlePlayClick() {
    alert(`Playing ${movieName}!`);
  }

  return (
    <Button onClick={handlePlayClick}>
      Play "{movieName}"
    </Button>
  );
}

function UploadButton() {
  return (
    <Button onClick={() => alert('Uploading!')}>
      Upload Image
    </Button>
  );
}

export default function Toolbar() {
  return (
    <div>
      <PlayButton movieName="Kiki's Delivery Service" />
      <UploadButton />
    </div>
  );
}

Tässä, Toolbar komponentti renderöi PlayButton komponentin sekä UploadButton komponentin:

  • PlayButton välittää handlePlayClick funktion onClick propsina Button:lle.
  • UploadButton välittää () => alert('Uploading!') funktion onClick propsina Button:lle.

Lopuksi, Button komponenttisi hyväksyy onClick propsin. Se välittää propsin suoraan selaimen sisäänrakennettuun <button> elementtiin onClick={onClick} koodilla. Tämä kertoo Reactille, että kutsuu välitettyä funktiota klikkauksen yhteydessä.

Jos käytät design system:iä, on yleistä komponenttien kuten painikkeiden sisältää tyylit mutta ei käyttäytymistä. Sen sijaan komponentit kuten PlayButton ja UploadButton välittävät tapahtumakäsittelijät alaspäin.

Tapahtumakäsittelijän propsien nimeäminen

Sisäänrakennetut elementit kuten <button> ja <div> tukevat ainoastaan selaimen tapahtumien nimiä kuten onClick. Kuitenkin, kun rakennat omia komponenttejasi, voit nimetä niiden tapahtumakäsittelijöiden propsit miten haluat.

Tavanomaisesti, tapahtumakäsittelijän propsien kuuluisi alkaa sanalla on, ja jatkua isolla kirjaimella.

Esimerkiksi, Button komponentin onClick propsi voitaisiin kutusua onSmash:

function Button({ onSmash, children }) {
  return (
    <button onClick={onSmash}>
      {children}
    </button>
  );
}

export default function App() {
  return (
    <div>
      <Button onSmash={() => alert('Playing!')}>
        Play Movie
      </Button>
      <Button onSmash={() => alert('Uploading!')}>
        Upload Image
      </Button>
    </div>
  );
}

Tässä esimerkissä, <button onClick={onSmash}> kertoo, että selaimen <button> elementti (pienin kirjaimin) tarvitsee silti propsin nimeltään onClick, mutta kustomoidun Button komponentin vastaanottaman propsin nimi voi olla mikä tahansa!

Kun komponenttisi tukee useampia interaktioita, saatat nimetä tapahtumakäsittelijöiden propsit sovelluskohtaisin konseptein. Esimerkiksi tämä Toolbar komponentti vastaanottaa onPlayMovie sekä onUploadImage tapahtumakäsittelijät:

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>
  );
}

Huomaa, miten App komponentin ei tarvitse tietää mitä Toolbar tekee sen onPlayMovie tai onUploadImage tapahtumakäsittelijöillä. Se on Toolbar komponentin toteutusyksityiskohta. Tässä, Toolbar välittää ne Button:nien onClick käsittelijöinä, mutta se voisi myöhemmin myös kutsua niitä pikanäppäimestä. Propsien nimeäminen sovelluskohtaisten vuorovaikutusten kautta, kuten onPlayMovie, antaa joustavuuden muuttaa niitä myöhemmin.

Huomaa

Varmista, että käytät asianmukaisia HTML-tageja tapahtumankäsittelijöillesi. Esimerkiksi klikkausten käsittelyyn käytä <button onClick={handleClick}> -elementtiä sen sijaan, että käyttäisit <div onClick={handleClick}>. Todellisen selaimen <button> -elementin käyttäminen mahdollistaa sisäänrakennetut selaimen toiminnot, kuten näppäimistönavigoinnin. Jos et pidä oletusarvoisesta painikkeen ulkoasusta ja haluat muokata sitä näyttämään enemmän linkiltä tai erilaiselta käyttöliittymäelementiltä, voit saavuttaa sen CSS:n avulla. Lue lisää saavutettavan merkinnän kirjoittamisesta.

Tapahtuman leviäminen

Komponenttisi tapahtumankäsittelijät nappaavat tapahtumia myös alakomponenteista. Tätä usein kutsutaan “kuplimiseksi” tai “propagoinniksi”: se alkaa sieltä missä tapahtuma esiintyi ja nousee puussa ylemmäs.

Tämä <div> sisältää kaksi painiketta. Sekä <div> tagi että painikkeet omaavat ikioman onClick käsittelijän. Mitä arvelet mitä tapahtumakäsittelijää kutsutaan, kun painiketta klikataan?

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <button onClick={() => alert('Playing!')}>
        Play Movie
      </button>
      <button onClick={() => alert('Uploading!')}>
        Upload Image
      </button>
    </div>
  );
}

Jos klikkaat jompaa kumpaa painiketta, sen onClick suoritetaan ensin, jonka jälkeen <div> tagin onClick. Joten kaksi viestiä tulee näkyviin. Jos klikkaat työkalupalkkia vain <div>:n onClick suoritetaan.

Sudenkuoppa

Kaikki tapahtumat propagoituvat Reactissa paitsi onScroll, joka toimii vain siinä JSX tagissa johon sen liität.

Propagoinnin pysäyttäminen

Tapahtumakäsittelijät vastaanottavat tapahtumaolion niiden ainoana argumenttina. Tavanomaisesti sitä usein kutsutaan e kirjaimella, joka on lyhenne sanasta “event”. Voit käyttää tätä oliota lukeaksesi tietoa tapahtumasta.

Tämä tapahtumaolio mahdollistaa myös tapahtuman propagoinnin estämisen. Jos haluat estää tapahtuman propagoinnin pääkomponenteille, kutsu e.stopPropagation() funktiota kuten tämä Button komponentti tekee:

function Button({ onClick, children }) {
  return (
    <button onClick={e => {
      e.stopPropagation();
      onClick();
    }}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <Button onClick={() => alert('Playing!')}>
        Play Movie
      </Button>
      <Button onClick={() => alert('Uploading!')}>
        Upload Image
      </Button>
    </div>
  );
}

Kun klikkaat painiketta:

  1. React kutsuu <button> tagille annettua onClick käsittelijää.
  2. Tämä Button:ssa määritelty käsittelijä tekee seuraavat asiat:
    • Kutsuu e.stopPropagation() funktiota, estäen tapahtuman kuplimisen.
    • Kutsuu onClick funktiota, joka on Toolbar komponentista välitetty propsi.
  3. Toolbar komponentissa määritelty funktio näyttää painikkeen oman ilmoituksen.
  4. Sillä propagointi on pysäytetty, <div> tagin onClick käsittelijää ei suoriteta.

e.stopPropagation() funktion tuloksena painikkeiden klikkaaminen näyttää vain yhden ilmoituksen (button:sta) kahden ilmoituksen sijaan (<button>:sta sekä <div>:sta). Painikkeen klikkaaminen ei ole sama asia kuin ympäröivä työkalupalkki, joten propagoinnin pysäyttäminen on järkevää tälle UI:lle.

Syväsukellus

Nappaa tapahtumavaiheet

Harvinaisissa tapauksissa saatat haluta napata kaikki lapsielementtien tapahtumat, vaikka ne olisivat estäneet propagoinnin. Esimerkiksi, ehkäpä haluat kerätä analytiikkatietoja jokaisesta klikkauksesta, riippumatta propagointilogiikasta. Voit tehdä tämän lisäämällä Capture tapahtumanimen perään:

<div onClickCapture={() => { /* suoritetaan ensin */ }}>
<button onClick={e => e.stopPropagation()} />
<button onClick={e => e.stopPropagation()} />
</div>

Jokainen tapahtuma propagoituu kolmaessa vaiheessa:

  1. Se kulkee alaspäin, kutsuen kaikki onClickCapture käsittelijät.
  2. Se suorittaa klikatun elementin onClick käsittelijän.
  3. Se kulkee ylöspäin, kutsuen kaikki onClick käsittelijät.

Tapahtumien nappaaminen on kätevää koodille kuten reitittimille taikka analytiikalle, mutta et todennäköisesti tule käyttämään sitä sovelluskoodissa.

Käsittelijöiden välittäminen vaihtoehtona propagoinnille

Huomaa kuinka tämä klikkauskäsittelijä suorittaa yhden rivin koodia ja sitten kutsuu välitettyä onClick propsia:

function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}

Voisit halutessasi lisätä lisää koodia tähän käsittelijään ennen onClick tapahtumakäsittelijän kutsumista. Tämä tapa mahdollistaa vaihtoehdon propagoinnille. Sen avulla alakomponentit käsittelevät tapahtuman, silti antaen pääkomponenttien määritellä lisäkäyttäytymisiä. Toisin kuin propagointi, se ei ole automaattista. Kuitenkin tällä tavalla voit selkeästi seurata koko koodiketjua, jota kutsutaan jonkin tapahtuman seurauksena.

Jos nojaat propagointiin ja on hankalaa jäljittää mikä käsittelijä suoritetaan ja miksi, kokeile tätä tapaa sen sijaan.

Oletustoiminnon estäminen

Jotkin selaintapahtumat sisältävät oletustoimintoja. Esimerkiksi kun <form>:ssa olevaa painiketta klikataan, submit -tapahtuma lataa koko sivun uudelleen oletusarvoisesti:

export default function Signup() {
  return (
    <form onSubmit={() => alert('Submitting!')}>
      <input />
      <button>Send</button>
    </form>
  );
}

Voit kutsua tapahtumaolion e.preventDefault() funktiota estääksesi tämän tapahtumasta:

export default function Signup() {
  return (
    <form onSubmit={e => {
      e.preventDefault();
      alert('Submitting!');
    }}>
      <input />
      <button>Send</button>
    </form>
  );
}

Älä sekoita e.stopPropagation() ja e.preventDefault() funktioita. Molemmat ovat hyödyllisiä, mutta ne eivät liity toisiinsa:

Voiko tapahtumakäsittelijöillä olla sivuvaikutuksia?

Totta kai! Tapahtumakäsittelijät ovat paras paikka sivuvaikutuksille.

Toisin kuin renderöintifunktiot, tapahtumakäsittelijöiden ei tarvitse olla puhtaita, joten ne ovat hyvä hyvä paikka muuttaa jotain. Esimerkiksi, syöttökentän arvon muuttaminen kirjoituksen seurauksena, tai listan muuttaminen painikkeen painalluksen seurauksena. Kuitenkin, jotta voit muuttaa jotain tietoa, se täytyy ensiksi tallentaa. Reactissa tämä tapahtuu käyttämällä tilaa, komponentin muistia. Tulet oppimaan siitä kaiken seuraavalla sivulla.

Kertaus

  • Voit käsitellä tapahtumia välittämällä funktion propsina elementille kuten <button>.
  • Tapahtumakäsittelijät tulee välitää, ei kutsua! onClick={handleClick}, ei onClick={handleClick()}.
  • Voit määritellä tapahtumakäsittelijän funktion erikseen tai samalla rivillä.
  • Tapahtumakäsittelijät määritellään komponentin sisällä, jotta ne saavat pääsyn propseihin.
  • Voit määritellä tapahtumakäsittelijän yläkomponentissa ja välittää sen propsina alakomponentille.
  • Voit määritellä omian tapahtumakäsittelijäpropseja sovelluskohtaisilla nimillä.
  • Tapahtumat propagoituvat ylöspäin. Kutsu e.stopPropagation() estääksesi sen.
  • Tapahtumilla saattaa olla ei toivottuja oletustoimintoja. Kutsu e.preventDefault() estääksesi sen.
  • Tapahtumakäsittelijän kutsuminen alakomponentista on hyvä vaihtoehto propagoinnille.

Haaste 1 / 2:
Korjaa tapahtumakäsittelijä

Painikkeen painamisen on tarkoitus vaihtaa sivun taustaväriä valkoisen ja musta välillä. Klikattaessa mitään ei kuitenkaan tapahdu. Korjaa ongelma. (Älä huoli handleClick:n sisällä olevasta logiikasta, se on hyvä sellaisenaan.)

export default function LightSwitch() {
  function handleClick() {
    let bodyStyle = document.body.style;
    if (bodyStyle.backgroundColor === 'black') {
      bodyStyle.backgroundColor = 'white';
    } else {
      bodyStyle.backgroundColor = 'black';
    }
  }

  return (
    <button onClick={handleClick()}>
      Kytke valot
    </button>
  );
}