Stav React dnes: od tříd k hookům a dál

Poslední aktualizace: 04/22/2026
  • Stav React musí být považován za neměnný, s aktualizacemi prováděnými pomocí setterů, nikoli přímých mutací, zejména u objektů a polí.
  • Aktualizace stavu jsou asynchronní a lze je dávkově provádět, takže použití funkčních aktualizátorů zabraňuje problémům se zastaralým stavem v časovačích, uzávěrkách a rychlých interakcích.
  • Funkční komponenty s hooky (useState, useRef a podobné) jsou moderním standardem, zatímco nástroje jako React.memo a Immer pomáhají s výkonem a vnořenými daty.
  • Jasné oddělení vlastností a stavu a model toku dat shora dolů udržují chování komponent předvídatelné při škálování aplikací.

Koncept správy stavu React

Stav je jeden z těch konceptů v Reactu, které na první pohled vypadají jednoduše, ale s růstem aplikace se rychle stávají složitými. Začnete s malým čítačem a pak se najednou potýkáte s více poli formuláře, asynchronními aktualizacemi, vnořenými objekty a problémy s výkonem, když se vše znovu vykresluje najednou. Hluboké pochopení stavu je to, co odlišuje někoho, kdo „používá React“, od někoho, kdo umí škálovat a ladit reálné React aplikace.

V této příručce si projdeme aktuální stav Reactu (slovní hříčka záměrná), od komponent tříd a metod životního cyklu až po moderní hooky a neměnné aktualizace. Také se ponoříme do jemných, ale klíčových témat, jako jsou asynchronní aktualizace, zastaralé uzávěry, kdy použít useRef místo useState a jak udržet uživatelské rozhraní předvídatelné. Cílem je poskytnout vám jasný mentální model, aby se vaše komponenty chovaly přesně tak, jak očekáváte.

Od rekvizit ke stavu: co vlastně kam patří?

Props a stav v Reactu

Jádrem každé komponenty React jsou dva hlavní zdroje dat: props a state. Podpory jsou předávány z nadřazené komponenty a zůstávají neměnné po celou dobu životnosti daného renderu, zatímco stát je vlastněn a řízen samotnou komponentou a je určen pro data, která se v čase mění.

Dobré pravidlo zní: pokud jsou data konfigurována zvenčí a v této komponentě se nemění, jedná se o prop; pokud je komponenta musí sledovat a aktualizovat, jedná se o stav. Představte si blikající textovou komponentu: skutečný text je poskytnut pouze jednou (prop), ale to, zda je aktuálně zobrazený nebo skrytý, se neustále přepíná (stav). Toto rozlišení umožňuje Reactu udržet tok dat předvídatelný a jednosměrný.

React podporuje jednosměrný tok dat shora dolů, kde se stav nachází v nejbližším společném předkovi, který ho potřebuje řídit. Nadřazená komponenta může uchovávat stav a předávat hodnoty jako props podřízeným komponentám, které je mohou vykreslovat nebo transformovat, ale nemusí vědět, zda tyto hodnoty původně pocházejí ze stavu, jiných props nebo byly pevně zakódovány.

Proto často uslyšíte, že stav je „lokální“ nebo „zapouzdřený“. Pouze komponenta, která vlastní část stavu, ji může změnit a jakékoli uživatelské rozhraní odvozené z tohoto stavu prochází směrem dolů prostřednictvím props. Můžete volně kombinovat stavové a bezstavové (čisté) komponenty a to, zda je něco stavové, je považováno za implementační detail, který se může v průběhu času měnit.

Komponenty třídy: stav a životní cyklus postaru

Před Hooky byl jediný způsob, jak používat metody stavu a životního cyklu v Reactu, s komponentami třídy ES6. Přestože většina moderních aplikací spoléhá na funkční komponenty, v mnoha kódových základech se stále setkáte (a někdy i udržujete) s komponentami tříd, takže stojí za to pochopit, jak fungují.

Chcete-li převést funkční komponentu, jako je jednoduchá Clock do třídy postupujete podle několika mechanických kroků. Vytvoříte třídu, která rozšiřuje React.Component, přidat render() metodu, přesunout tělo funkce do render, vyměnit props s this.propsa smažte původní funkci. Dokud React bude pokračovat v renderování <Clock /> do stejného uzlu DOM, znovu použije jednu instanci dané třídy.

Přidání lokálního stavu do třídy znamená definovat konstruktor a přiřadit mu iniciálu. this.state objekt. Například byste mohli přesunout date hodnota z props do stavu přidáním konstruktoru, který volá super(props) a sady this.state = { date: new Date() }, a poté nahrazení jakéhokoli použití this.props.date in render() s this.state.dateNezapomeňte, že v komponentách třídy byste měli přiřazovat přímo pouze this.state uvnitř konstruktoru.

Metody životního cyklu jsou speciální metody tříd, které React volá v určitých bodech životního cyklu komponenty. Když je komponenta poprvé vložena do DOMu (připojena), React volá componentDidMount()Když je odebrán (odpojen), React volá componentWillUnmount()V klasickém příkladu tikajících hodin nastavíte časovač v componentDidMount a vyčistěte to componentWillUnmount, uložení ID časovače na this (například this.timerId) a volání this.setState() každou sekundu pro aktualizaci času.

Typický životní cyklus těchto hodin vypadá takto: React volá konstruktor pro inicializaci stavu, poté render() k vytvoření DOM, pak componentDidMount() kde spouštíte časovač. Pokaždé, když se časovač spustí, zavoláte setState(), který zařadí aktualizaci do fronty a spustí render() s novým stavem. Jakmile je komponenta odstraněna, componentWillUnmount() vynuluje časovač, abyste neztráceli zdroje.

Správné řízení stavu ve třídách také znamená respektovat tři důležitá pravidla o setState. Nesmíš mutovat this.state přímo, je třeba si uvědomit, že aktualizace mohou být asynchronní a dávkové, a měli byste si uvědomit, že aktualizace jsou povrchně sloučeny (slučují se pouze stavové klíče nejvyšší úrovně, nikoli hluboce vnořené objekty).

Správné používání stavu: mutace, asynchronní aktualizace a tok dat

Jedním z největších zdrojů zmatku pro začátečníky je to, že setState (a ekvivalent Hooku) neaktualizuje stav okamžitě a nikdy byste neměli měnit stavové objekty na místě. React často kvůli výkonu sdružuje více aktualizací dohromady, takže obě this.state ve třídách a stavové proměnné v hookech nemusí odrážet konečný stav ihned po naplánování aktualizace.

Přímo mutující stav, jako např. this.state.count++ nebo úprava vlastností objektu stavu přeskočí detekci změn v Reactu a může způsobit, že komponenty zůstanou zaseknuté na starých hodnotách. React očekává, že s jakýmkoli objektem ve stavu budete zacházet jako s objektem pouze pro čtení. Místo změny existujících objektů vytvoříte nový objekt nebo pole s požadovanými změnami a předáte je aktualizátoru stavu.

Protože aktualizace stavu mohou být asynchronní, musíte být opatrní při výpočtu dalšího stavu z předchozího. Ve třídách něco jako this.setState({ count: this.state.count + 1 }) může být chybné, pokud je dávkově provedeno více aktualizací. Oprava spočívá v použití funkčního tvaru: this.setState((prevState, props) => ({ count: prevState.count + 1 }))Tím je zaručeno, že pracujete s nejnovějším snímkem stavu.

Stejný vzorec existuje i u hooků: aktualizační program můžete volat s funkcí místo hodnoty. Například, setCount(prev => prev + 1) je bezpečnější způsob, jak zvýšit hodnotu čítače, pokud nová hodnota závisí na předchozí nebo pokud k aktualizacím může docházet uvnitř časovačů nebo obslužných rutin událostí, které se spustí později.

I když je stav „lokální“, efekt změny stavu se vždy projeví ve stromové struktuře komponent. Opětovné vykreslení rodičovské položky spuštěné aktualizací stavu ve výchozím nastavení znovu vykreslí i všechny své podřízené položky. Tento tok dat shora dolů je základem mentálního modelu Reactu: jeden zdroj pravdy nahoře, z něj odvozené uživatelské rozhraní níže.

Moderní React: Hooky a funkční komponenty

Od verze React 16.8 se hooky staly standardním způsobem pro správu stavu a vedlejších efektů ve funkčních komponentách. Umožňují vám používat stejné funkce, které měly komponenty tříd (a další), bez nutnosti psaní tříd nebo práce s nimi. this a metody životního cyklu výslovně, apoyándose en el estado estable de JavaScript moderno.

Funkční komponenty jsou nyní výchozím stylem v kódových základech Reactu. Místo psaní class Example extends React.Componentdefinujete prostou funkci jako function Example() { return <div />; }Když potřebujete stav, vedlejší efekty nebo reference, „připojíte se“ k Reactu pomocí funkcí jako useState, useEffect a useRefHooky nelze použít uvnitř tříd a musí respektovat Pravidla pro hooky (vždy je volejte na nejvyšší úrovni komponenty, nikdy ne v smyčkách nebo podmínkách).

Jedno useState Hook je nejjednodušší způsob, jak přidat lokální stav funkční komponentě. Jako argument bere počáteční hodnotu a vrací dvojici: aktuální hodnotu stavu a setter. Díky destrukturalizaci polí obvykle napíšete něco jako const = useState(0)React si tento stav zachovává mezi opakovanými vykresleními, což znamená, že funkci lze volat mnohokrát, ale hodnota stavu se pamatuje.

Na rozdíl od stavu třídy, hodnota, kterou uchováváte v useState nemusí být objekt. Můžete ukládat čísla, řetězce, booleovské hodnoty, pole nebo objekty – cokoli, co odpovídá datům. Pokud potřebujete více nezávislých hodnot, můžete zavolat funkci useState několikrát (například age, fruit, todos). Alternativně můžete uložit jeden objekt a spravovat v něm více vlastností, ale při aktualizaci musíte respektovat pravidla neměnnosti.

Když zavoláte funkci setter, která je vrácena funkcí useState, neměníte hodnotu synchronně; zařazujete aktualizaci do fronty stejně jako u setState ve třídách. Při dalším renderování React přiřadí vaší komponentě novou hodnotu stavu. Proto načtení stavu ihned po volání setteru uvnitř stejné synchronní funkce stále vrátí starou hodnotu.

Správa objektů a vnořených dat ve stavu

React umožňuje uvést do stavu libovolnou hodnotu JavaScriptu, včetně objektů a polí, ale musíte s nimi zacházet jako s neměnnými snímky (snapshoty). Primitivní hodnoty jako čísla a řetězce stejně nelze mutovat, ale objekty a pole technicky vzato ano – jejich mutace však narušuje předpoklady Reactu a může vést k jemným chybám, kdy se komponenty neaktualizují.

Uvažujme stavový objekt jako { x: 0, y: 0 } reprezentující pozici ukazatele. Pokud píšete position.x = event.clientX přímo jste změnili existující objekt. React nemá tušení, že se hodnota změnila, protože jste nikdy nevolali setter, takže se znovu nevykreslí a vaše uživatelské rozhraní zůstane zaseknuté. Správný přístup je setPosition({ x: event.clientX, y: event.clientY }), který vytvoří zcela nový objekt a řekne Reactu, aby s ním vykreslil.

Lokální mutace čerstvě vytvořených objektů je naprosto v pořádku. Například můžete krok za krokem vytvořit nový objekt: const next = { ...prev }; next.city = 'Paris'; pokud next nebyl již ve stavu. Mutace se stává problémem pouze tehdy, když změníte objekt, který se již používá v nějakém předchozím snímku stavu, protože ostatní části vaší aplikace se mohou stále spoléhat na tuto starou hodnotu.

Chcete-li aktualizovat pouze část objektu a zachovat zbytek, obvykle se používá syntaxe rozložení objektu. Pro objekt stavu formuláře, jako je { firstName, lastName, email }, změny vstupu můžete zpracovat něčím jako setPerson({ ...person, : event.target.value })Tím se zkopírují staré vlastnosti a poté se přepíše pouze ta, která se změnila. Rozložení je mělké, takže vnořené objekty vyžadují větší péči.

Hluboce vnořené objekty mohou rychle vést k podrobnému aktualizačnímu kódu, protože je nutné vytvářet nové kopie na každé úrovni cesty, kterou měníte. Například, pokud person.artwork.city změny, udělali byste setPerson({ ...person, artwork: { ...person.artwork, city: 'London' } })Pod kapotou neexistuje žádný „vnořený objekt“; existují samostatné objekty, které na sebe navzájem odkazují, takže pokud více rodičovských objektů ukazuje na stejný podřízený objekt a vy ho změníte, měníte data na více než jednom místě najednou.

Pokud neustále píšete vnořené rozložení, můžete zvážit zploštění tvaru stavu nebo použití pomocné knihovny, jako je Immer. Immer umožňuje psát kód, který vypadá mutativní (jako draft.artwork.city = 'London'), zatímco pro vás v zákulisí vytvoří novou neměnnou kopii. V Reactu můžete spárovat Immer s hooky pomocí useImmer z use-immer balíček.

Stav v praxi: formuláře, časovače a uživatelský vstup

V reálných aplikacích zřídka spravujete stav pouze pro čítače; spravujete uživatelský vstup, odpovědi API a „režimy“ uživatelského rozhraní, jako je načítání, chyba a úspěch. Klíčovou změnou v myšlení u Reactu je, že se „neupravuje DOM“ (například „nevypíná toto tlačítko“); místo toho se popisuje, jak by mělo uživatelské rozhraní vypadat pro každý stav, a poté se stav aktualizuje.

Například komponenta kvízu nebo formuláře může sledovat status stav, který přepíná mezi 'typing', 'submitting' a 'success'. JSX podmíněně deaktivuje tlačítko pro odeslání během odesílání a zobrazí zprávu o úspěšném odpovědi, jakmile je odpověď správná. Nikdy nevoláte imperativní DOM metody – React jednoduše znovu vykreslí s novým stavem a vizuální výstup se změní.

Práce s poli formuláře je moment, kdy se mnoho vývojářů poprvé setkává s rozdílem mezi slučováním stavů tříd a... useState chování. Ve třídě, setState sloučí objekt, který předáte, s existujícím stavovým objektem, takže aktualizace jednoho pole neodstraní ostatní. S useState, aktualizace nahradí celou hodnotu: pokud je váš stav objekt a zavoláte setState({ email: '...' }), jakékoli další vlastnosti (například password) zmizí, pokud je ručně nesloučíte.

Tento rozdíl lidi podrazí, když provádějí refaktoring z více primitivních stavových proměnných na jeden objekt. Pokud se změníte z const a const na const a pak napsat generický setForm({ : value }), skončíte se stavovým objektem, který má vždy pouze jedno pole. Oprava spočívá v rozšíření předchozího objektu: setForm({ ...form, : value }).

V komplexnějších aplikacích často nebudete volat setState (nebo setSomething) přímo odkudkoli. Stav můžete centralizovat pomocí knihoven jako Redux nebo MobX, nebo použít useReducer Zapojení pro stavové automaty na úrovni komponent. V těchto nastaveních se stále uplatňují stejné principy neměnnosti; jediný rozdíl je v tom, kde a jak se provádějí aktualizace.

Opětovné vykreslení, výkon a kdy použít useRef

Každá aktualizace stavu v Reactu spustí opětovné vykreslení komponenty, která daný stav vlastní, a ve výchozím nastavení i všech jejích podřízených komponent. To je záměrné: opakované vykreslování je způsob, jakým se vaše uživatelské rozhraní synchronizuje s aktuálními daty. Znamená to ale také, že bezmyšlenkovité umístění stavu může způsobit zbytečnou práci a pomalé uživatelské rozhraní, zejména když podřízené komponenty provádějí nákladné výpočty nebo vykreslují velké seznamy.

Představte si aplikaci se vstupním polem a samostatnou komponentou, která zobrazuje dlouhý seznam dovedností. Pokud nadřazená komponenta vlastní jak text, který uživatel píše, tak i samotný seznam, pak každý stisk klávesy znovu vykreslí celý strom, včetně seznamu dovedností, i když se tento seznam nezměnil. To je zbytečná námaha.

Jeden jednoduchý způsob, jak to optimalizovat, je zabalit podřízené komponenty do React.memo. React.memo je komponenta vyššího řádu, která si ukládá do paměti výsledek funkční komponenty: pokud jsou její vlastnosti mezi rendery stejné, React přeskočí jejich opětovné renderování. Takže vaše komponenta seznamu dovedností, jakmile je zabalena do React.memo, se nebude znovu vykreslovat při každém stisknutí klávesy – pouze když skills vlastnosti se skutečně mění (například když přidáte novou dovednost).

Ne všechna „stavovská“ data patří do useStateněkdy useRef je lepší nástroj. Jedno useRef Hook vám poskytne měnitelný objekt s current vlastnost, která přetrvává po celou dobu životnosti komponenty, ale její aktualizace ano ne spustit opětovné vykreslení. Díky tomu je ideální pro ukládání věcí, jako jsou ID časovačů, odkazy na prvky DOM nebo čítače, které chcete sledovat, ale nemusíte je zobrazovat v uživatelském rozhraní.

Jednoduchým příkladem je čítač implementovaný s useRef místo useState. Pokud uložíte počet do countRef.current a inkrementujete ji v obslužné rutině události, interní hodnota se změní, ale zobrazený JSX se neaktualizuje, protože React neprovedl re-rendering. Toto ilustruje zásadní rozdíl: useState je pro hodnoty, které řídí uživatelské rozhraní; useRef je pro hodnoty, které chcete zachovat bez ovlivnění vykreslování.

Neměnnost a proč je přímá mutace pastí

Základním principem v Reactu je, že aktualizace stavu musí být neměnné. To neznamená, že nikdy nemůžete nic změnit; znamená to, že místo úpravy existujících hodnot (zejména objektů a polí) vytvoříte nové a ty staré necháte jako historické snímky vašeho uživatelského rozhraní.

Přímá mutace stavu přeruší spojení mezi vaším mentálním modelem a tím, co React dělá. Pokud uděláte něco jako state.count++ nebo vložením hodnoty přímo do stavového pole React nepozná, že se něco změnilo, protože jste nikdy nevolali funkci updater. Interní snapshot, který React používá k rozhodnutí, kdy znovu vykreslit, zůstává stejný, zatímco váš kód si myslí, že se hodnota změnila. Takhle vznikají chyby, které se „samy opraví“ při opětovném načtení.

Také se musíte vyhnout přiřazení hodnoty stavu jiné proměnné a následné změně této proměnné. Například, dělat const newCount = count; newCount++; stále mutuje stejnou základní hodnotu pro primitiva a pro objekty, const copy = stateObj; vůbec nevytváří kopii – pouze vytváří další odkaz na stejný objekt. Správné kopírování vyžaduje vzory jako { ...stateObj } pro objekty nebo pro pole.

Knihovny jako Redux, MobX (pokud jsou nakonfigurovány pro neměnnost) nebo Immer existují částečně proto, aby vynucovaly nebo zjednodušovaly neměnné vzory. Ať už používáte vestavěné hooky Reactu nebo knihovnu pro správu stavů, platí zlaté pravidlo: nikdy neměňte existující stav, pokud očekáváte, že React změnu zachytí a znovu provede renderování.

Asynchronní aktualizace, dávkování a zastaralý stav

Jedním nenápadným, ale zásadním detailem ohledně stavu React je, že aktualizace jsou asynchronní a plánované, neaplikují se okamžitě. Když zavoláte setState nebo nastavovač háčku jako setCountReact „zařadí“ renderování na nějakou dobu v budoucnu. Neblokuje váš kód hned na místě pro aktualizaci a renderování, což umožňuje Reactu dávkově provádět více aktualizací a udržovat plynulý výkon.

Tento model plánování znamená, že se nemůžete spoléhat na čtení stavu ihned po volání aktualizačního programu uvnitř stejného synchronního bloku. Hodnota, kterou získáte, bude obvykle starý snímek. Místo toho byste si měli aktualizační program představit jako požadavek: „při příštím renderování použijte tuto hodnotu (nebo tuto transformační funkci)“.

To je obzvláště důležité, když aktualizujete stav na základě jeho aktuální hodnoty z closures, jako je setTimeout nebo zpětná volání předplatného. Tyto zpětné volání zachycují stav v době jejich vytvoření. Pokud to pak uděláte setCount(count + 1) uvnitř časového limitu, count na který odkazujete, může být zastaralý v době, kdy se zpětné volání skutečně spustí.

Tento jev je známý jako „zastaralý stav“ nebo „zastaralé uzávěrky“. Například pokud máte tlačítko, které po kliknutí volá funkci, která nastaví časový limit a poté po jedné sekundě zvýší stav, opakované rychlé kliknutí nemusí stav zvýšit správně. Každé zpětné volání časového limitu používá starý count zachytilo se to v době, kdy byl naplánován časový limit.

Robustní opravou je použití funkční aktualizační formy vašeho nástroje pro nastavení stavu. Místo setCount(count + 1) uvnitř časového limitu píšete setCount(prevCount => prevCount + 1)Nyní každé zpětné volání obdrží nejnovější předchozí hodnotu v okamžiku provedení aktualizace, nikoli tu, která se nacházela v rozsahu platnosti v době vytvoření časového limitu. Tím se eliminuje problém se zastaralým stavem, aniž by se jinak změnilo chování vašich uzávěrů.

Dokumentace Reactu také upozorňuje na méně známý detail: pokud váš funkční updater nic nevrací (undefined), React přeskočí opětovné vykreslování. To znamená, že vaše aktualizační funkce by měly vždy vracet další hodnotu stavu (nebo znovu použít předchozí), pokud explicitně nechcete aktualizaci zabránit – což je u standardního useState používání.

Pochopení této kombinace asynchronního plánování, dávkování a chování při uzavírání je klíčové pro psaní spolehlivé stavové logiky v aplikacích, které se zabývají časovými limity, intervaly, předplatnými nebo rychlými interakcemi uživatelů. Jakmile si osvojíte, že nastavovače stavů plánují aktualizace, místo aby je prováděly okamžitě, chyby, které se dříve zdály náhodné, začnou dávat smysl.

Když se všechny tyto myšlenky spojí – props vs. state, životní cykly tříd vs. hooky, neměnnost, řízené komponenty, useRef pro nevizuální hodnoty, memoizaci, asynchronní aktualizace a zastaralé uzávěry – nakonec získáte výkonný a předvídatelný model pro to, jak se uživatelská rozhraní Reactu v čase vyvíjejí. Místo přemýšlení o nezbytných změnách DOMu navrhujete modely s jasným stavem a necháváte React na zpracování opětovného vykreslování, což usnadňuje uvažování o vašich komponentách, jejich testování a rozšiřování s růstem vaší aplikace.

estado etable de javascript 2025
Související článek:
Stabilní stav moderního JavaScriptu
Související příspěvky: