Tilan jakaminen komponenttien välillä

Joskus haluat kahden komponentin tilan muuttuvan yhdessä. Tehdäksesi tämän, poista tila molemmista komponenteista ja siirrä se niiden lähimpään pääkomponenttiin ja välitä se komponenteille propsien kautta. Tätä kutsutaan tilan nostamiseksi ylös, ja se on yksi yleinen tapa jota tulet tekemään Reactia kirjoittaessasi.

Tulet oppimaan

  • Miten jakaa tilaa komponenttien välillä “nostamalla ne ylös”
  • Mitä ovat hallitut ja hallitsemattomat komponentit

Esimerkkinä tilan nostaminen

Tässä esimerkissä, Accordion pääkomponentti renderöi kaksi erillistä Panel -komponenttia:

  • Accordion
    • Panel
    • Panel

Kullakin Panel komponentilla on totuusarvo tyyppinen isActive tila, joka päättää onko sen sisältö näkyvissä.

Paina Näytä -painiketta molemmista paneeleista:

import { useState } from 'react';

function Panel({ title, children }) {
  const [isActive, setIsActive] = useState(false);
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={() => setIsActive(true)}>
          Näytä
        </button>
      )}
    </section>
  );
}

export default function Accordion() {
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel title="About">
        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
      </Panel>
      <Panel title="Etymology">
        The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
      </Panel>
    </>
  );
}

Huomaa miten yhden paneelin painikkeen painaminen ei vaikuta toiseen paneeliin—ne ovat toisistaan riippumattomia.

Kaavio, joka näyttää puurakennelman kolmesta komponentista, yksi pääkomponentti on nimetty Parent, ja kaksi alakomponenttia on nimetty Panel. Molemmat Panel komponentit sisältävät isActive:n arvolla false.
Kaavio, joka näyttää puurakennelman kolmesta komponentista, yksi pääkomponentti on nimetty Parent, ja kaksi alakomponenttia on nimetty Panel. Molemmat Panel komponentit sisältävät isActive:n arvolla false.

Aluksi, kunkin Panel komponentin isActive tila on false, joten molemmat ovat suljettuina.

Sama kaavio kuin edellinen, mutta ensimmäisen paneelin isActive on korostettu osoittaen klikkausta. Ensimmäisen paneelin isActive arvo on true. Toinen Panel -komponentin sisältää silti arvon false.
Sama kaavio kuin edellinen, mutta ensimmäisen paneelin isActive on korostettu osoittaen klikkausta. Ensimmäisen paneelin isActive arvo on true. Toinen Panel -komponentin sisältää silti arvon false.

Kummankin Panel komponentin painikkeen painaminen päivittää vain sen Panel komponentin isActive tilan yksinään.

Muutta sanotaa, että haluat muuttaa sen niin, että vain yksi paneeli voi olla avattuna kerrallaan. Tällä suunnitelmalla, toisen paneelin avaamisen tulisi sulkea ensimmäinen paneeli. Miten toteuttaisit tämän?

Koordinoidaksesi nämä kaksi paneelia, sinun täytyy “nostaa tila ylös” pääkomponenttiin kolmessa eri vaiheessa:

  1. Poista tila lapsikomponentista.
  2. Välitä kovakoodattu data yhteisestä pääkomponentista.
  3. Lisää tila yhteiseen pääkomponenttiin ja välitä se alas tapahtumakäsittelijöiden kanssa.

Tämä antaa Accordion komponentin koordinoida molemmat Panel komponentit ja pitää vain yhtä auki kerrallaan.

1. Vaihe: Poista tila lapsikomponenteista

Luovutat Panel komponentin isActive ohjauksen sen pääkomponentille. Tämä tarkoittaa, että pääkomponentti välittää isActive:n Panel komponentille propsien kautta. Aloita poistamalla tämä rivi Panel komponentista:

const [isActive, setIsActive] = useState(false);

Ja sen sijaan lisää isActive Panel komponentin propsilistaan:

function Panel({ title, children, isActive }) {

Nyt Panel komponentin pääkomponentti ohjaa isActive:a välittämällä sen alas propsina. Lisäksi Panel komponentilla ei ole määräysvaltaa isActive tilan arvoon—se on täysin pääkomponentin vastuulla.

2. Vaihe: Välitä kovakoodattu data yhteisestä pääkomponentista

Tilan nostamiseksi ylös, sinun täytyy etsiä molempien komponenttien lähin jaettu pääkomponetti:

  • Accordion (lähin yhteinen komponentti)
    • Panel
    • Panel

Tässä esimerkissä se on Accordion komponentti. Sillä se sijaitsee molmepien paneelien yläpuolella ja se voi ohjata niiden propseja, siitä tulee “totuuden lähde” sille kumpi paneeli on aktiivinen. Välitä Accrdion komponentista kovakoodattu isActive (esimerkiksi, true) molemmille paneeleille:

import { useState } from 'react';

export default function Accordion() {
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel title="About" isActive={true}>
        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
      </Panel>
      <Panel title="Etymology" isActive={true}>
        The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
      </Panel>
    </>
  );
}

function Panel({ title, children, isActive }) {
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={() => setIsActive(true)}>
          Show
        </button>
      )}
    </section>
  );
}

Kokeile muokata kovakoodattua isActive arvoa Accordion komponentissa ja katso mitä ruudulla tapahtuu.

3. Vaihe: Lisää tila yhteiseen pääkomponenttiin

Tilan nostaminen ylös usein muuttaa sen luonnetta, mitä tallennat tilana.

Tässä tapauksessa vain yhden paneelin tulisi olla aktiivinen kerralla. Tämä tarkoittaa, että yhteisen Accordion pääkomponentin tulisi pitää kirjaa siitä, mikä paneeli on aktiivinen. boolean arvon sijaan, se voisi olla numero, joka vastaa aktiivisen Panel komponentin indeksiä:

const [activeIndex, setActiveIndex] = useState(0);

Kun activeIndex on 0, ensimmäinen paneeli on aktiivinen. Kun activeIndex on 1, toinen paneeli on aktiivinen.

“Show” painikkeen painaminen kummassakin Panel komponentissa tulisi muuttaa aktiivista indeksiä Accordion komponentissa. Panel ei voi asettaa activeIndex tilaa suoraan, sillä se on määritelty Accordion komponentissa. Accordion komponentin täytyy eksplisiittisesti sallia Panel komponentin muuttaa activeIndex tilaa välittämällä tapahtumakäsittelijä propsina:

<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
</>

Panel komponentin sisällä oleva <button> käyttää nyt onShow propsia sen tapahtumakäsittelijänä:

import { useState } from 'react';

export default function Accordion() {
  const [activeIndex, setActiveIndex] = useState(0);
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel
        title="About"
        isActive={activeIndex === 0}
        onShow={() => setActiveIndex(0)}
      >
        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
      </Panel>
      <Panel
        title="Etymology"
        isActive={activeIndex === 1}
        onShow={() => setActiveIndex(1)}
      >
        The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
      </Panel>
    </>
  );
}

function Panel({
  title,
  children,
  isActive,
  onShow
}) {
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={onShow}>
          Show
        </button>
      )}
    </section>
  );
}

Tämä viimeistelee tilan nostamisen ylös! Tilan siirtäminen yhteiseen pääkomponenttiin mahdollistaa kahden paneelin koordinoimisen. Aktiivisen indeksin käyttäminen kahden “onko näkyvissä” muutujan sijaan varmistaa, että vain yksi paneeli on aina aktiivinen. Tapahtumakäsittelijän välittäminen alakomponentille mahdollistaa sen, että alakomponentti voi muuttaa pääkomponentin tilaa.

Kaavio, joka näyttää kolme komponenttia, yhden pääkomponentin nimeltään Accrodion ja kaksi alakomponenttia nimeltään Panel. Accordion sisältää activeIndex-arvon nolla, joka muuttuu ensimmäiselle paneelille välitetyksi isActive-arvoksi true, ja toiselle paneelille välitetyksi isActive-arvoksi false.
Kaavio, joka näyttää kolme komponenttia, yhden pääkomponentin nimeltään Accrodion ja kaksi alakomponenttia nimeltään Panel. Accordion sisältää activeIndex-arvon nolla, joka muuttuu ensimmäiselle paneelille välitetyksi isActive-arvoksi true, ja toiselle paneelille välitetyksi isActive-arvoksi false.

Aluksi, Accordion komponentin activeIndex on 0, joten ensimmäinen Panel komponentti vastaanottaa isActive = true

Sama kaavio kuin aiemmin, jossa Accordion pääkomponentin activeIndex on korostettuna osoittaen klikkausta, arvolla yksi. Virtaus kahteen alakomponenttiin on myös korostettu, ja välitetty isActive-arvo on muutettu päinvastaiseksi: false ensimmäiselle paneelille ja true seuraavalle.
Sama kaavio kuin aiemmin, jossa Accordion pääkomponentin activeIndex on korostettuna osoittaen klikkausta, arvolla yksi. Virtaus kahteen alakomponenttiin on myös korostettu, ja välitetty isActive-arvo on muutettu päinvastaiseksi: false ensimmäiselle paneelille ja true seuraavalle.

Kun Accordion komponentin activeIndex tila muuttuu arvoksi 1, toinen Panel komponentti sen sijaan vastaanottaa isActive = true

Syväsukellus

Hallitut ja hallitsemattomat komponentit

On yleistä kutsua komponenttia, jotka sisältävät paikallista tilaa, “hallitsemattomiksi”. Esimerkiksi, alkuperäinen Panel komponentti, joka sisälsi isActive tilamuuttujan on hallitsematon, koska sen pääkomponentti ei voi vaikuttaa onko paneeli avoin vai suljettu.

Vastaavasti voidaan sanoa, että komponentti on “ohjattu” kun sen tärkeät tiedot on ohjattavissa propsien kautta komponentin oman tilan sijaan. Tämä antaa pääkomponentin määritellä täysin sen käyttäytymisen. Viimeisin Panel komponentti isActive propsilla on Accordion komponentin ohjaama.

Hallitsemattomat komponentit ovat helpompia käyttää niiden pääkomponenteissa, sillä ne vaativat vähemmän määrittelyä. Ne eivät kuitenkaan ole yhtä joustavia kun haluat koordinoida niitä yhdessä. Hallitut komponentit ovat mahdollisimman joustavia, mutta vaativat pääkomponentin määrittelemään ne täysin propsien kautta.

Käytännössä, “hallitut” ja “hallitsemattomat” eivät ole tarkkoja teknisiä termejä—kullakin komponentilla on sekoitus paikallista tilaa ja propseja. Kuitenkin, tämä on hyödyllinen tapa keskutella siitä miten komponentit ovat suunniteltu ja mitä toimintoja ne tarjoavat.

Kun kirjoitat komponenttia, harkitse mitä tietoa tulisi ohjata propsien kautta, ja minkä tiedon tulisi olla komponentin paikallista tilaa. Mutta voit kuitenkin aina muuttaa mieltäsi ja muuttaa komponenttia myöhemmin.

Yksi totuuden lähde jokaiselle tilalle

React sovelluksessa, monilla komponenteilla on niiden oma tila. Jotkin tilat saattavat “asua” lähempänä lehtikomponentteja (komponetit puun alaosassa) kuten syöttökentissä. Toiset tilat saattavat “asua” lähempänä sovelluksen juurta. Esimerkiksi, selain-puolen reitityskirjastot ovat usein toteuttettu tallentamalla senhetkinen polku Reactin tilaan ja sen välittäminen alas propseilla!

Kullekin uniikille tilan palaselle valitset komponentin, joka “omistaa” sen. Tätä käytäntöä kutsutaan “yhdeksi totuuden lähteeksi”. Se ei tarkoita, että kaikki tilat ovat samassa paikassa—vaan, että jokaiselle tilan palaselle on tietty komponentti, joka pitää tiedon yllä. Sen sijaan, että monistaisit jaetun tilan komponenttien välillä, nostat tilan ylös niiden lähimpään jaettuun pääkomponenttiin, ja välität sen alas alakomponeteille, jotka sitä tarvitsevat.

Sovelluksesi muuttuu kun työstät sitä. On yleistä, että siirrät tilaa alas tai takaisin ylös kun vielä mietit missä yksittäiset tilan palaset “asuvat”. Tämä on sa prosessia!

Nähdäksesi mitä tämä tarkoittaa käytännössä muutamin komponentein, lue Ajattelu Reactissa.

Kertaus

  • Kun haluat koordinoida kahta komponenttia, siirrä niiden tila yhteiseen pääkomponenttiin.
  • Välitä sitten tieto pääkomponentista propseilla.
  • Lopuksi, välitä tapahtumakäsittelijät alas, jotta alakomponentit voivat muuttaa pääkomponentin tilaa.
  • On hyödyllistä ajatella komponentteja “hallittuina” (ohjataan propseilla) tai “hallitsemattomina” (ohjataan tilalla).

Haaste 1 / 2:
Synkronoidut tulot

Nämä kaksi syöttökenttää ovat toisistaan riippumattomia. Tee niistä synkronoituja: yhden muuttaminen päivittää toisen samalla tekstillä ja päin vastoin.

import { useState } from 'react';

export default function SyncedInputs() {
  return (
    <>
      <Input label="First input" />
      <Input label="Second input" />
    </>
  );
}

function Input({ label }) {
  const [text, setText] = useState('');

  function handleChange(e) {
    setText(e.target.value);
  }

  return (
    <label>
      {label}
      {' '}
      <input
        value={text}
        onChange={handleChange}
      />
    </label>
  );
}