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.
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:
- Poista tila lapsikomponentista.
- Välitä kovakoodattu data yhteisestä pääkomponentista.
- Lisää tila yhteiseen pääkomponenttiin ja välitä se alas Tapahtumankä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ä Tapahtumankä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 Tapahtumankä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. Tapahtumankäsittelijän välittäminen alakomponentille mahdollistaa sen, että alakomponentti voi muuttaa pääkomponentin tilaa.
Syväsukellus
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ä Tapahtumankä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> ); }