Ez az oldal az állapot és életciklus fogalmait mutatja be egy React komponensben. A részletes komponens API referenciát itt találod.
Vedd a ketyegő óra példát az egyik korábbi fejezetből. Az Elemek renderelése fejezetben csak egyetlen módját tanultuk meg a felhasználói felület frissítésének. A ReactDOM.render() metódus meghívásával megváltoztatjuk a renderelt kimenetet:
function tick() {
const element = (
<div>
<h1>Helló, világ!</h1>
<h2>Az idő {new Date().toLocaleTimeString()}.</h2>
</div>
)
ReactDOM.render(element, document.getElementById('root'))
}
setInterval(tick, 1000)Ebben a fejezetben megtanuljuk, hogy hogyan tudjuk a Clock komponenst igazán újrafelhasználhatóvá és egységbe foglalttá tenni. Saját időzítőt fog beállítani, hogy minden másodpercben frissíteni tudja önmagát.
Kezdhetjük azzal, hogy hogyan foglaljuk egységbe azt, ahogyan az óra kinéz:
function Clock(props) {
return (
<div>
<h1>Helló, világ!</h1>
<h2>Az idő {props.date.toLocaleTimeString()}.</h2>
</div>
)
}
function tick() {
ReactDOM.render(<Clock date={new Date()} />, document.getElementById('root'))
}
setInterval(tick, 1000)Azonban ebből hiányzik valami nagyon fontos: Az a tény, hogy a Clock komponens beállít egy időzítőt és minden másodpercben frissíti a felhasználói felületet, a Clock komponens saját implementációs részlete kell hogy legyen.
Ideális esetben ezt egyszer szeretnénk megírni és hagyjuk a Clock-ot saját magát frissíteni:
ReactDOM.render(<Clock />, document.getElementById('root'))Ennek az implementálásához szükségünk lesz egy "állapot"-ra ("state") a Clock komponensben.
Az állapot hasonló a prop-okhoz, de privát a komponensre nézve, és teljes mértékben irányított a komponens által.
Függvény konvertálása osztállyá
Egy függvény komponenst, mint például a Clock-ot, ebben az öt lépésben tudsz osztállyá konvertálni:
- Készíts egy ES6 osztályt ugyanazzal a névvel és terjeszd ki a
React.Componentosztályt. - Adj hozzá egy üres
render()metódust. - Helyezd át a a függvény testét a
render()metódusba. - Nevezd át a
props-otthis.props-ra arender()testében. - Töröld a megmaradt üres függvény deklarációt.
class Clock extends React.Component {
render() {
return (
<div>
<h1>Helló, világ!</h1>
<h2>Az idő {this.props.date.toLocaleTimeString()}.</h2>
</div>
)
}
}A Clock most már osztályként van definiálva függvény helyett.
A render metódus minden alkalommal meg lesz hívva ha egy frissítés történik, de amíg a <Clock />-ot ugyanabba a DOM csomópontba rendereljük, addig a Clock osztálynak csupán egy példánya lesz használva. Ez lehetővé teszi olyan funkciók hozzáadását mint a helyi állapot és életciklus metódusok.
Helyi állapot hozzáadása egy osztályhoz
Helyezzük át a date objektumot a props-ból a state-be három lépésben:
- Nevezd át a
this.props.date-etthis.state.date-re arender()metódusban:
class Clock extends React.Component {
render() {
return (
<div>
<h1>Helló, világ!</h1>
<h2>Az idő {this.state.date.toLocaleTimeString()}.</h2>
</div>
)
}
}- Adj hozzá egy osztály konstruktort, ami hozzárendel egy kezdetleges
this.state-et:
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = { date: new Date() }
}
render() {
return (
<div>
<h1>Helló, világ!</h1>
<h2>Az idő {this.state.date.toLocaleTimeString()}.</h2>
</div>
)
}
}Figyeld meg, hogy hogyan adjuk át az alapkonstruktornak a props-ot:
constructor(props) {
super(props);
this.state = {date: new Date()};
}Az osztálykomponensek konstruktorai mindig meg kell hogy hívják az alapkonstruktort a props átadásával.
- Töröld ki a
dateprop-ot a<Clock />elemből:
ReactDOM.render(<Clock />, document.getElementById('root'))Az időzítő kódját később adjuk vissza a komponensbe.
Az eredmény így néz ki:
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = { date: new Date() }
}
render() {
return (
<div>
<h1>Helló, világ!</h1>
<h2>Az idő {this.state.date.toLocaleTimeString()}.</h2>
</div>
)
}
}
ReactDOM.render(<Clock />, document.getElementById('root'))A következőben hagyjuk, hogy a Clock maga állítson be egy időzítőt és frissítse magát minden másodpercben.
Életciklus metódusok hozzáadása egy osztályhoz
Sok komponenssel rendelkező alkalmazásokban nagyon fontos, hogy a komponensek által elfoglalt erőforrásokat felszabadítsuk, amikor azok elpusztulnak.
Szeretnénk felállítani egy időzítőt, amikor a Clock először renderelődik DOM-ba. A Reactben ezt hívjuk "előkészítés"-nek, vagy "mounting"-nak.
Azt is szeretnénk, ha az időzítő törölve lenne, amikor a DOM által készített Clock el lesz távolítva. A React-ben ezt hívjuk "leválasztás"-nak vagy "unmounting"-nak.
A komponens oszályban tudunk speciális metódusokat deklarálni, amik lefuttatnak egy kódot amikor a komponens előkészül, vagy leválik:
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = { date: new Date() }
}
componentDidMount() {}
componentWillUnmount() {}
render() {
return (
<div>
<h1>Helló, világ!</h1>
<h2>Az idő {this.state.date.toLocaleTimeString()}.</h2>
</div>
)
}
}Ezeket a metódusokat "életciklus" metódusoknak" hívjuk.
A componentDidMount() metódus azután fut le, hogy a komponens kimenete a DOM-ba lett renderelve. Ez egy jó hely az időzítő beállítására:
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}Vedd észre, hogy az időzítő azonosítóját közvetlenül a this-re mentjük (this.timerID).
Míg a this.props-ot maga a React állítja fel, és a this.state-nek speciális jelentése van, te nyugodtan adhatsz hozzá manuálisan egyéb mezőket, ha valamit tárolni szeretnél, ami nem vesz részt az adatfolyamban (mint például az időzítő azonosító).
Az időzítőt a componentWillUnmount() életciklus metódusban fogjuk leállítani:
componentWillUnmount() {
clearInterval(this.timerID);
}Végezetül implementálni fogunk egy tick() metódust, amit a Clock komponens fog futtatni minden másodpercben.
Ez a this.setState() metódus segítségével fogja a komponens helyi állapotát frissíteni.
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = { date: new Date() }
}
componentDidMount() {
this.timerID = setInterval(() => this.tick(), 1000)
}
componentWillUnmount() {
clearInterval(this.timerID)
}
tick() {
this.setState({
date: new Date(),
})
}
render() {
return (
<div>
<h1>Helló, világ!</h1>
<h2>Az idő {this.state.date.toLocaleTimeString()}.</h2>
</div>
)
}
}
ReactDOM.render(<Clock />, document.getElementById('root'))Az óra most már minden másodpercben kettyen.
Vegyük át gyorsan mi is történik és a metódusok milyen sorrendben vannak meghívva:
-
Amikor a
<Clock />-ot átadjuk aReactDOM.render()metódusnak, a React meghívja aClockkomponens konstruktorát. Mivel aClockkomponensnek meg kell jelenítenie a jelenlegi időt, ez inicializál egythis.state-et, ami egy objektumot tartalmaz a jelenlegi idővel. Később ezt az állapotot frissítjük. -
Ezután a React meghívja a
Clockkomponensrender()metódusát. A React ennek segítségével állapítja meg, hogy mit kell mutatnia a képernyőn. A React ezután frissíti a DOM-ot, hogy az megegyezzen aClockrender kimenetével. -
Amikor a
Clockkimenet be van illesztve a DOM-ba, a React meghívja acomponentDidMount()életciklus metódust. Ezen belül aClockkomponens megkéri a böngészőt, hogy az állítson fel egy időzítőt, ami minden másodpercben meghívja a komponenstick()metódusát. -
A böngésző minden másodpercben meghívja a
tick()metódust. Ezen belül, aClockkomponens beütemez egy kezelői felület frissítést asetState()meghívásával egy objektummal, ami a jelenlegi időt tartalmazza. AsetState()hívásnak köszönhetően a React tudja, hogy az állapot megváltozott, és újra meghívja arender()metódust, hogy megtudja, minek kéne megjelennie a képernyőn. Ezúttal athis.state.datearender()metódusban más lesz, és ezért a render kimenete tartalmazni fogja a frissítet időt. A React ennek megfelelően frissíti a DOM-ot. -
Ha a
Clockkomponens el lesz távolítva a DOM-ból, a React meghívja acomponentWillUnmount()életciklus metódust és az időzítő így megáll.
Az állapot helyes használata
Három dolog van, amit tudnod kell a setState() metódusról.
Ne módosítsd az állapotot közvetlenül
Például ez nem fogja újrarenderelni a komponenst:
// Helytelen
this.state.comment = 'Helló'Használd helyette a setState()-t:
// Helyes
this.setState({ comment: 'Helló' })Az egyetlen hely, ahol bármit is hozzárendelhetsz a this.state-hez, az a konstruktor.
Az állapot frissítések lehetnek aszinkronok
A React összefoghat egy csomó setState() hívást egy szimpla frissítésbe a teljesítmény növelése érdekében.
Mivel a this.props és a this.state frissülhet aszinkron módon, nem szabad az értékeikre hagyatkoznod a következő állapot kiszámításához.
Például ez a kód lehet, hogy nem fogja tudni frissíteni a számlálót:
// Helytelen
this.setState({
counter: this.state.counter + this.props.increment,
})Hogy ezt kijavítsd, használd a setState() másik formáját, ami egy függvényt fogad argumentumként egy objektum helyett. A függvény fogadja az előző állapotot első argumentumként, valamint az előző props-ot másodikként:
// Helyes
this.setState((state, props) => ({
counter: state.counter + props.increment,
}))A fentiekben egy nyíl függvényt használtunk, de ez működne egy átlagos függvénnyel is:
// Helyes
this.setState(function (state, props) {
return {
counter: state.counter + props.increment,
}
})Az állapot frissítések egyesítve vannak
Amikor meghívod a setState() metódust, a React egyesíti az általad szolgáltatott objektumot a jelenlegi állapottal.
Például az állapotod tartalmazhat számos független változót:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}Ezek aztán függetlenül frissíthetőek különálló setState() hívásokkal:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}Az egyesítés sekély, tehát a this.setState({comments}) érintetlenül hagyja a this.state.posts-ot, de teljesen lecseréli a this.state.comments-et.
Az adat lefelé folyik
Sem a felnőtt, sem a gyermek komponens nem tudhatja, hogy egy bizonyos komponens állapotteljes vagy állapot nélküli, és az sem kell hogy érdekelje őket, hogy függvényként vagy osztályként van-e definiálva.
Ezért van az, hogy az állapotot gyakran hívjuk helyinek, vagy egységbe zártnak. Nem hozzáférhető semelyik másik komponensből, csak abból amelyik birtokolja és beállítja.
Egy komponens dönthet úgy, hogy leküldi a saját állapotát prop-ként a gyermek komponenseinek:
<FormattedDate date={this.state.date} />A FormattedDate komponens megkapja a date-et a props-ban, és nem tudja, hogy az a Clock állapotából, a Clock prop-jából jött, vagy kézzel lett beírva:
function FormattedDate(props) {
return <h2>Az idő {props.date.toLocaleTimeString()}.</h2>
}Ezt közismerten "felülről lefelé irányuló", vagy egyirányú adatfolyamnak hívjuk. Egy adott állapotot mindig csak egy bizonyos komponens birtokolhat, és ez az állapot csakis a komponensfában ‘alatta lévő’ komponensek adataira vagy megjelenésére hathat.
Ha úgy képzelsz el egy komponensfát, mint a prop-ok vízesését, minden komponens állapota olyan, mint egy plusz vízforrás, ami tetszőleges pontokon belecsatlakozik a lefelé haladó áramlatba.
Hogy megmutassuk azt, hogy minden komponens tényleg teljesen izolált, készíthetünk egy App komponenst, ami három <Clock>-t renderel:
function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))Minden Clock beállítja a saját időzítőjét és ezek egymástól függetlenül frissülnek.
Az, hogy egy React komponens állapotteljes vagy állapot nélküli, a saját implementációs részletének tekinthető, ami idővel változhat. Emiatt használhatsz állapot nélküli komponenseket állapotteljes komponenseken belül, és ugyanígy fordítva is.