Jak správně používat useEffect a useState v Reactu

  • useState spravuje lokální stav ve funkčních komponentách a useEffect synchronizuje komponentu s externími systémy po každém vykreslení.
  • Efekty mohou být bez sanitace (logika se spouští pouze po vykreslení) nebo se sanitací, vracející funkci čištění pro odběry a intervaly.
  • Pole závislostí useEffect řídí, kdy se efekt spustí, a je klíčem k zamezení nekonečným smyčkám a problémům s výkonem.
  • Správná kombinace useState a useEffect umožňuje vytvářet HTTP požadavky, ošetřovat chyby a oddělovat složitou logiku do nezávislých, udržovatelných efektů.

React hooky useEffect a useState

Pokud pracujete s Reactem denně, dříve či později zjistíte, že Zvládnutí useState a useEffect dělá velký rozdíl mezi komponentou, která „funguje přerušovaně“, a solidním, udržovatelným a snadno laditelným rozhraním. Oba hooky se zpočátku zdají jednoduché, ale jakmile začnete vytvářet HTTP požadavky, odběry nebo časovače, objeví se pochybnosti, efekty běží ve smyčkách a stavy se stávají „zastaralými“.

V následujících řádcích uvidíme Jak správně používat useState a useEffect v Reactu, od základů až po pokročilé vzoryTento kurz integruje osvědčené postupy pro výkon, správu závislostí, čištění efektů, použití s ​​externími API a některé triky, jak se vyhnout běžným chybám. Cílem je, abyste na konci byli schopni číst jakoukoli komponentu s hooky a rozumět tomu, co se děje, a především abyste si byli schopni navrhnout vlastní komponentu bez obav z poškození.

Co přesně jsou useState a useEffect a kdy byste je měli použít?

React zavedl hooky od verze 16.8. Spravovat logiku stavů a ​​životního cyklu v rámci funkčních komponent bez použití tříd. Cílem je umožnit deklarativní tvorbu opakovaně použitelné logiky s využitím samotného jazyka JavaScript (uzávěry, čisté funkce atd.).

Háček useState Používá se k přidání lokální stav funkční komponentyMísto toho, aby tento.stát y this.setStateProhlašujete pár [valor, setValor] React přiřadí pořadí volání hooků v dané komponentě k danému stavu. Každý render bude mít svou vlastní „verzi“ daného stavu.

Pro jeho část, useEffect je hák určený pro vedlejší účinky.kód, který se provede poté, co React vykreslil v DOMu a který interaguje s externími systémy: požadavky na data, předplatné websocketů, přístup k localStorage, manipulace s okny, časovače s setInterval o setTimeout, Etc.

Ačkoli se často říká, že useEffect je ekvivalentní kombinaci metod componentDidMount, componentDidUpdate a componentWillUnmount.Přesnější je si myslet, že popisuje „Co je potřeba synchronizovat s exteriérem po každém renderování“Pak pole závislostí definuje kdy Tato synchronizace je hotová.

Stav a efekty v Reactu

useState: deklarativní správa lokálního stavu

Háček useState Je deklarován na začátku komponenty a obdrží počáteční hodnotaVrací pole se dvěma pozicemi: aktuální hodnotou a funkcí pro její aktualizaci. Něco tak jednoduchého, jako je čítač, je definováno takto:

Základní příklad useState:

import { useState } from "react";

funkce ZákladníCenter() {
const[počet, nastavitPočet] = použítStav(0);

konstantní přírůstek = () => {
nastavitPočet(počet + 1);
};

vrátit se (

Stiskli jste tlačítko {count}krát
Stiskni mě

);
}

V každém renderu, počet představuje stav odpovídající danému konkrétnímu vykresleníPři volání setCountReact naplánuje nové vykreslení s aktualizovanou hodnotou. Pokud aktualizace závisí na předchozí hodnotě, je vhodnější použít přístup s funkcemi:

setCount(prev => prev + 1);

Tato syntaxe zabraňuje problémům při řetězení několika aktualizací, protože React zaručuje, že prev je poslední stabilní hodnota.i v souběžném režimu.

useEffect: Pochopení jeho životního cyklu a pole závislostí

Podpis efektového háku je velmi jednoduchý: useEffect(efecto, dependencias?)Prvním argumentem je instalační funkce, která se spustí po vykreslení DOMu. Volitelně může tato funkce vrátit další funkci pro vyčištění, kterou React zavolá před opětovným spuštěním efektu nebo při odpojení komponenty.

Klíčem k efektivnímu používání useEffect je pochopení pole závislostí.Toto pole udává, na kterých „reaktivních“ hodnotách závisí logika efektu: props, stavy a proměnné nebo funkce definované v komponentě. React porovnává aktuální závislosti se závislostmi z předchozího renderu pomocí Object.isPokud dojde ke změně, nejprve spusťte předchozí čištění a poté nakonfigurujte nový efekt.

V závislosti na tom, jak toto pole zapíšete, se projeví různé chování:

  • Žádný druhý argumentEfekt se provede po každé vykreslení.
  • Prázdné pole []Efekt se spustí automaticky jednou po prvním renderování (režim „componentDidMount“ a „componentWillUnmount“).
  • Pole se závislostmi [a, b]Efekt se provede po prvním vykreslení a pokaždé, když se jedna z těchto závislostí změní.

Velmi typický příklad: Aktualizace názvu dokumentu pomocí počítadlaJe to moderní funkční ekvivalent klasického třídního vzoru s componentDidMount + componentDidUpdate.

Příklad synchronizace názvu dokumentu pomocí useEffect:

import { useState, useEffect } from "react";

funkce PočítadloSNázvem() {
const[počet, nastavitPočet] = použítStav(0);

použitíEfektu(() => {
document.title = `Stiskli jste tlačítko ${count}krát`;
}, [počet]);

vrátit se (

Tlačítko bylo stisknuto {count}krát
setCount(počet + 1)}>Zatlačte mě

);
}

V tomto případě závislost je counta musí to být v poli, ať se děje cokoli.protože se používá v efektu. Pokud byste se ho pokusili vynechat, eslint-plugin-react-hooks Varovalo by vás to; a pokud byste donutili linter k zastavení, skončili byste s těžko odhalitelnými chybami, kde název neodpovídá skutečné hodnotě čítače.

Pokročilé příklady použitíEfekt

Nedezinfekční efekty: logika, kterou je třeba spustit až po vykreslení

Existuje kategorie efektů, které nevyžadují explicitní čištěníTo jsou ty, ve kterých „Uděláš něco a zapomeneš na to“: přihlásit se přes konzoli, upravit název dokumentu, provést jednorázový HTTP požadavek, změřit něco pouze jednou atd.

Ve světě společenských tříd byla tato logika rozšířena mezi componentDidMount y componentDidUpdate, často s duplicitním kódem. S hooky tato duplicita mizí, protože Jeden useEffect zvládne jak počáteční vykreslení, tak aktualizace..

Jednoduchý příklad efektu bez sanitace: získat polohu uživatele pomocí geolokačního API prohlížeče a uložte jej ve stavu:

import { useState, useEffect } from "react";

funkce PolohaUživatele() {
const [zeměpisná šířka, setLatitude] = useState(0);
const [délka, nastavitDélku] = useState(0);

použitíEfektu(() => {
pokud (!navigator.geolokace) {
console.log("Prohlížeč nepodporuje geolokaci");
návrat;
}

navigator.geolocation.getCurrentPosition((pos) => {
setLatitude(pozice.souřadnice.latitude.toFixed(0));
setLength(pozice.souřadnice.délka.naPevnou(0));
});
}, []);

V tomto případě Chceme požádat o umístění pouze jednou, při montáži komponenty.Pole závislostí je proto prázdné: nic, co by mělo efekt znovu spustit. Není potřeba žádné čištění, protože jsme nenechali žádné aktivní intervaly, odběry ani posluchače.

Dalším běžným příkladem účinku bez nápravy je protokol nebo analytický sledovač při změně určitých informacíNapříklad zaznamenávání, ve které chatovací místnosti se uživatel nachází nebo kolik položek má v košíku, pokud s touto událostí není udržováno živé připojení.

Důsledky sanitace: odběry, intervaly a úniky paměti

Druhou významnou kategorií je efekty, které je třeba vyčistitObvykle se to stává, když vytváříte něco, co by mělo odpojit nebo zrušit když se změní závislosti nebo je komponenta odpojena: předplatné API, globální posluchače událostí, časovače, webové sockety atd.

Vzor je vždy stejný: V rámci efektu inicializujete zdroj a vrátíte funkci, která jej zruší.React zavolá tuto funkci pro vyčištění před opětovným spuštěním efektu a když komponenta zmizí z DOMu.

Klasický příklad: čítač, který se automaticky zvyšuje v intervalu.

import { useState, useEffect } from "react";

const AutomatickýPočítadlo = () => {
const [čítač, setCounter] = useState(0);

použitíEfektu(() => {
konstantní interval = nastavitInterval(() => {
console.log("Interval...");
setCounter((c) => c + 1);
}, 2000 XNUMX);

vrátit () => {
console.log("Interval mazání");
vymazatInterval(interval);
};
}, []);

návrat {čelit} ;
};

Zde se interval vytvoří při připojení komponenty a odstraní se při jejím odpojení. Pokud zapomenete zavolat clearInterval, Interval by pokračoval, i když by se komponenta již nezobrazovala.což způsobuje úniky paměti a aktualizace stavu u disassemblovaných komponent.

Dalším velmi častým vzorem je předplatné na globální akceJak resize de window o keydown en document:

import { useEffect } from "react";

funkce GlobálníUdálosti() {
použitíEfektu(() => {
const handleResize = () => {
console.log("Změněna velikost okna");
};

const handleKeyDown = (událost) => {
console.log(«Stisknutá klávesa», event.klávesa);
};

window.addEventListener("změna velikosti", handleResize);
document.addEventListener("stisknutí klávesy", handleKeyDown);

vrátit () => {
window.removeEventListener("změna velikosti", handleResize);
document.removeEventListener("stlačení klávesy", handleKeyDown);
};
}, []);

návrat Naslouchání globálnímu dění ;
}

La Čištění zanechá systém přesně takový, jaký byl před instalací komponenty.Toto zrcadlení mezi nastavením a čištěním je zásadní, zejména vzhledem k tomu, že v režimu striktního vývoje může React spustit další cyklus nastavení → čištění → nastavení pro detekci chyb.

HTTP požadavky s useEffect a useState: podrobný návod

Jedno z nejčastějších použití useEffect es načíst data z API při připojení komponenty a výsledek uložit do stavu s useStateExistuje velmi praktický „recept“, který můžete použít téměř pokaždé:

  1. Komponenta se spustí v režimu „načítání“. s typovým stavem isLoading.
  2. Požadavek zadejte uvnitř useEffectu. hned poté, co React vykreslí komponentu.
  3. Uložit odpověď ve státě a značka isLoading jak false až skončím.
  4. Vykreslit podmíněně otáčející se symbol nebo zpráva o načítání během isLoading moře truea data, až budou připravena.

Kompletní příklad: Aplikace, která zobrazuje náhodné štěně pomocí Dog API.

import { useState, useEffect } from "react";

funkce NáhodnýPes() {
const [obrázekUrl, setObrázekUrl] = useState(null);
const[načítá se, setNačítá se] = useState(true);
const [chyba, setError] = useState(null);

použitíEfektu(() => {
const fetchDog = async() => {
Snaž se {
setIsLoading(true);
setError(null);

const response = await fetch(«https://dog.ceo/api/breeds/image/random»);

pokud (!response.ok) {
throw new Error(«Chyba HTTP» + response.status);
}

const data = await response.json();
setImageUrl(data.zpráva);
} catch (chyba) {
setError(chybová.zpráva);
} Konečně {
setIsLoading(false);
}
};

načteníDog();
}, []);

pokud (seNačítá) {
návrat Nabíjení… ;
}

pokud (chyba) {
návrat Chyba: {error} ;
}

vrátit se (



);
}

Všimněte si, že je to ověřeno response.ok rozlišovat mezi správnou odpovědí (kódy 200–299) a chybami HTTPFetch odmítá promise pouze kvůli selhání sítě, takže pokud to nezkontrolujete... ok Možná si myslíte, že „všechno je v pořádku“, i s pětistovkou.

Stejný vzorec lze upravit pro požadavky, které závisí na vlastnostech. Například pokud chcete zobrazit data konkrétního uživatele na základě userId který pochází shora, použili byste tuto vlastnost jako závislost efektu na aktualizovat data pokaždé, když se změní.

Požadavky závislé na vrtuli a bezpečné zrušení během demontáže

Pokud URL adresa nebo identita načtených dat závisí na prop (například znovu načtěte uživatele, pokud se to změní props.userId), je důležité:

  • Správně specifikujte vlastnost jako závislost. účinku.
  • Vyhněte se aktualizacím stavu, pokud je komponenta odebrána než žádost skončí.

Jednoduchý vzorec pro zrušení je použití interního příznaku:

import { useEffect, useState } from "react";
import axios from "axios";

funkce Uživatel({ IDUživatele }) {
const [uživatel, setUživatel] = useState(null);

použitíEfektu(() => {
nechť jePřipevněno = true;

const fetchData = async() => {
const výsledek = čekání na axios(
`https://jsonplaceholder.typicode.com/users/${userId}`
);

pokud (jePřipojeno) {
setUser(výsledek.data);
}
};

načteníData();

vrátit () => {
jePřipojeno = false;
};
}, [IDuživatele]);

pokud (!uživatel) {
návrat Nabíjení… ;
}

vrátit se (

{uživatelské_jméno}
E-mailová adresa: {user.email}
Telefon: {uživatel.telefon}

);
}

Zatímco jeMounted sea trueJe povoleno aktualizovat stavKdyž je komponenta odstraněna, efekt čištění nastaví tento příznak. falseA i když se slib později splní, setUser Již se to neprovede. Existují propracovanější alternativy (AbortController, datové knihovny atd.), ale tento ilustrativní vzor objasňuje myšlenku.

Logika opětovného použití: více efektů ve stejné komponentě

Jednou z velkých silných stránek háčků je, že Nejste omezeni na jeden useEffect na komponentu.Ve skutečnosti je to obvykle výhodnější. rozdělit logiku na několik specializovaných efektů místo toho, aby se to všechno smíchalo do jednoho obrovského celku.

Představte si komponentu, která má obojí účetní, vzdálená data a možná předplatnéVe třídě byste skončili s metodami životního cyklu plnými smíšeného kódu: v componentDidMount Spustili jste petici a podepsali jste ji; componentDidUpdate ručně jste zkontrolovali, co se změnilo; v componentWillUnmount Vyčistil jsi obě věci.

S háčky můžete použít jeden useEffect pro každý „nápad“ nebo odpovědnost:

import { useState, useEffect } from "react";

const MojeKomponenta = () => {
const[počet, nastavitPočet] = použítStav(0);
const [data, setData] = useState(null);

použitíEfektu(() => {
console.log(«Připojená komponenta»);
}, []);

použitíEfektu(() => {
console.log(`Počet se změnil na ${count}`);
}, [počet]);

použitíEfektu(() => {
console.log("Data se změnila");
}, [data]);

vrátit se (

setCount((c) => c + 1)}>Klikněte zde

);
};

To znamená, Každý efekt se zajímá jen o to, co ho zajímá.Kód se stává čitelnějším a je snazší přidávat nebo odebírat chování bez narušení zbytku. React aplikuje všechny efekty komponent v pořadí, v jakém je deklarujete.

Optimalizace výkonu: závislosti a efekty, které se nadměrně neopakují

Když efekt vykonává nákladnou práci (dotazy API, náročné výpočty, re-renderování třetí stranou…), Nechcete, aby se spouštěl při každé aktualizaci komponenty.V komponentě třídy se to často řešilo ručním porovnáváním. if (prevProps.x !== this.props.x) v componentDidUpdate.

S háčky, Optimalizace je zabudována do samotné signatury useEffect. pomocí pole závislostí. Například pokud chcete, aby se efekt spustil pouze při změně count, deklarujete to jako:

useEffect(() => {
console.log("Ha cambiado count");
}, [count]);

React porovná staré pole závislostí [valorAnteriorDeCount] s novým [valorNuevoDeCount]. Pokud jsou všechny prvky stejné, efekt se přeskočí.Pokud se nějaké liší, předchozí efekt se vyčistí (pokud k vyčištění došlo) a konfigurace se spustí znovu.

Podobně, pokud chcete efekt, který se spustí pouze jednou pro inicializaci něčeho (a volitelně pro jeho vyčištění při disassemblování), Používáte prázdné poleAle buďte opatrní: Propsy a stavy, které si v efektu přečtete, si zachovají své původní hodnoty.Proto byste tento vzorec měli použít, když jste si jisti, že váš efekt nezávisí na žádné změně.

Aby se předešlo přehlédnutím, důrazně se doporučuje pravidlo aktivovat exhaustive-deps de eslint-plugin-react-hooksToto pravidlo analyzuje kód efektu a upozorní vás, pokud chybí nějaké závislosti, a navrhne vhodnou opravu. Je to velmi efektivní způsob, jak se vyhnout drobným chybám při použití zastaralých hodnot stavu nebo vlastností.

Typické problémy s useEffect a jak se jim vyhnout

Práce s efekty je teoreticky jednoduchá, ale v praxi existuje řada... opakující se chyby což je něco, co je třeba mít vždy na paměti, abyste do nich znovu a znovu neupadali.

Jedním z nejdiskutovanějších je nekonečná smyčka renderováníStává se to, když tvůj efekt aktualizuje stav, který je součástí jeho závislostí způsobem, který nekonverguje. Například pokud máte:

useEffect(() => {
setCount(count + 1);
}, [count]);

V každém renderu, jako count Změní se, efekt se znovu spustí a vy znovu aktualizujete stav a tak dále donekonečna. V těchto případech jste buď na špatném místě (tato aktualizace by se neměla nacházet v efektu), nebo potřebujete jiné typy logiky, například interval s čištěnímnebo použijte funkční formu setCount(c => c + 1) bez uvedení count v prostorách.

Dalším problémem je pocit, že efekt „při montáži se to spustí dvakrát“V striktním režimu provádí React ve vývoji jakýsi „zátěžový test“ spuštěním setup → cleanup → setup hned na začátku. V produkčním prostředí se to sice neděje, ale Zvýrazňuje špatně navržené efekty, které se správně nečistí.Pokud je logika čištění symetrická s logikou nastavení, uživatel by si neměl všimnout ničeho neobvyklého.

Je také běžné vidět efekty, které se opakují v každém renderu, přestože existují závislosti. To se obvykle stává proto, že Pole obsahuje objekty nebo funkce vytvořené při každém vykreslení.Protože se reference neustále mění, React předpokládá, že se změnila i závislost. Řešení: deklarovat tyto funkce nebo objekty v samotném efektu nebo si je zapamatujte useCallback/useMemo pokud jsou venku opravdu potřeba.

useEffect, externí systémy a pokročilé použití s ​​událostmi efektů

Důvodem toho, proč je useEffect es Udržujte svou komponentu synchronizovanou s externími systémy: sockety, mapy, vložené video přehrávače, API prohlížeče atd. Vzor je vždy založen na myšlence „když se tyto závislosti změní, znovu se připojte k novým hodnotám a odpojte se od starých“.

Například komponenta ChatRoom který se v závislosti na konkrétní místnosti připojuje serverUrl y roomId měl by:

  • Připojte se během jízdy s první kombinací serverUrl y roomId.
  • Pokud uživatel změní místnosti nebo servery, odpojte se od předchozí místnosti a připojte se k nové.
  • Při demontáži součásti ji zcela odpojte.

S useEffect a dobrým vyčištěním toho můžete dosáhnout každý render má své souvislé spojení a že React dokáže opakovat sekvenci nastavení/čištění, aniž by cokoli zůstalo neaktivní.

V pokročilejších scénářích můžete potřebovat Přečíst nejnovější stav a vlastnosti efektu bez nutnosti opakovaného spuštění.A právě zde přicházejí na řadu takovéto vzory. Efekty událostí (háčky jako) useEffectEvent v nových doporučeních), které vám umožňují přesunout určitý nereaktivní kód ze seznamu závislostí. Praktickým přístupem je zároveň používejte vlastní reference nebo hooky pro ty skladby, kam klasické reaktivní chování dobře nesedí.

V kontextu vykreslování na straně serveru (SSR) mějte na paměti, že efekty běží pouze na klientoviPočáteční HTML kód se vykreslí na serveru a jakmile je aplikace v prohlížeči načtena, spustí se příkazy useEffect. Pokud potřebujete zobrazit něco jiného pouze na straně klienta, můžete použít stav jako didMount to prochází false a true v podstatě s [] a odtud upravte podmínky pro vykreslení.

Dominovat useState y useEffect V konečném důsledku jde o to, abyste si uvědomili, že každé vykreslení je snímek vaší komponenty, že hooky jsou vázány na pořadí, ve kterém je deklarujete, a že efekty popisují, jak synchronizovat tento snímek s vnějším světem, včetně toho, jak vrátit zpět to, co je třeba vrátit zpět. Díky dobré správě závislostí, čištění a oddělení efektů podle odpovědností budou vaše komponenty fungovat mnohem předvídatelněji, spotřebovávat méně zdrojů a bude podstatně snazší zjistit, co by mohlo být špatně, když se něco nechová tak, jak by mělo.


Přidat jako preferovaný zdroj v Googlu