A React Hooks — olyan függvények, amelyek lehetővé teszik a state, effektusok, kontextus és más React-képességek használatát funkcionális komponensekben, osztályok írása nélkül. A React 16.8-ban bevezetett hookok radikálisan megváltoztatták a fejlesztési megközelítést, felváltva az olyan bonyolult mintákat, mint a HOC és a render props. A React, 2024 szerint 15 beépített hook létezik, amelyek lefedik a komponenskezelés összes forgatókönyvét.
Főbb pontok
A React Hooks — a React-be épített függvények halmaza, amelyek lehetővé teszik a keretrendszer belső mechanizmusaihoz való ‘csatlakozást’ osztályok létrehozása nélkül. A React 16.8 előtt a funkcionális komponensek csak props-okat fogadhattak és JSX-et adhattak vissza — bármilyen összetett logika osztálykomponenst igényelt.
Az osztálykomponensek problémája — a kapcsolódó logika szétválasztása. Az egy entitáshoz tartozó kódot (például WebSocket feliratkozás) kényszerűen szét kellett osztani a componentDidMount, componentDidUpdate és componentWillUnmount között. A hookok ezt megoldják: az összes logika egy useEffect-ben csoportosul.
A második probléma — a logika újrafelhasználása. Az állapotlogika osztálykomponensek közötti újrafelhasználásához HOC (magasabb rendű komponensek) vagy render props alkalmazására volt szükség — mindkét minta extra beágyazást hozott létre a komponensfában. Az egyéni hookok ezt a problémát beágyazás nélkül oldják meg.
useState — a legalapvetőbb hook. Elfogad egy kezdeti értéket, és egy tuple-t ad vissza: az aktuális értéket és egy függvényt annak frissítésére. Ellentétben a this.setState-tel az osztályokban, a useState nem egyesíti az objektumokat — az új állapot teljesen felváltja az előzőt az adott useState híváshoz.
import React, { useState } from 'react';
function Form() {
const [text, setText] = useState('');
const [submitting, setSubmitting] = useState(false);
async function handleSubmit() {
setSubmitting(true);
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify({ text })
});
setSubmitting(false);
}
return (
<div>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={handleSubmit}
disabled={submitting}>
{submitting ? 'Saving...' : 'Submit'}
</button>
</div>
);
}
A useState setterje elfogadhat egy függvényt, amely megkapja az előző értéket és visszaadja az újat. A funkcionális forma kötelező, ha az új állapot az előzőből számítódik és kötegelt setState hívások lehetségesek — minden hívás a naprakész előző értéket kapja.
useEffect — hook mellékhatások végrehajtásához funkcionális komponensekben. Két argumentumot fogad: egy effektfüggvényt és egy függőségi tömböt. A React az effektet minden render után végrehajtja, de függőségek megadásakor — csak ha bármelyik megváltozott.
Egy üres függőségi tömb [] azt jelenti, hogy az effekt egyszer fut le az első render után (analóg a componentDidMount-tal). Egy függvény visszaadása az effektből a cleanup, amely a komponens eltávolításakor (analóg a componentWillUnmount-tal) és az effekt újrafuttatása előtt fut le.
import React, { useState, useEffect } from 'react';
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket(`wss://chat/${roomId}`);
socket.onmessage = (event) => {
setMessages(prev => [...prev, JSON.parse(event.data)]);
};
// Cleanup - socket bezárása
return () => socket.close();
}, [roomId]);
return <ul>
{messages.map((msg, i) =>
<li key={i}>{msg.text}</li>
)}
</ul>;
}
A useEffect függőségei — az egyetlen kapcsolat az effekt és a külső világ között. Ha az effektben egy props-ból vagy state-ből származó változót használunk, annak a függőségi tömbben kell lennie. Ennek a szabálynak a figyelmen kívül hagyása az elavult adatokkal (stale closure) kapcsolatos hibák forrása.
useContext — hook a kontextus értékének olvasásához. Elfogadja a React.createContext által létrehozott kontextusobjektumot és visszaadja az aktuális értéket. Ha a kontextus megváltozik, a komponens automatikusan újrarenderelődik az új értékkel.
A useContext megjelenése előtt a kontextus elérése funkcionális komponensekben Context.Consumer-be burkolást igényelt, ami beágyazást hozott létre. useContext teljesen megszünteti a Consumer-t — elég a hook-ot a funkcionális komponens bármely pontján meghívni.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('világos');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button
className={`btn btn-${theme}`}
>
{theme === 'dark' ? 'moon' : '☀️'}
</button>;
}
A useContext nem helyettesíti a Redux-ot vagy a globális állapotot. A React a kontextust ritkán változó értékekre ajánlja – téma, nyelv, hitelesítés. Gyakran frissülő adatok esetén minden újrarender újraszámítja a kontextus összes fogyasztóját, ami csökkentheti a teljesítményt.
useReducer — alternatíva a useState-hez összetett frissítési logikával rendelkező állapotokhoz. Elfogad egy reducer függvényt és egy kezdeti állapotot, visszaadja az aktuális állapotot és egy dispatch függvényt. A reducer megkapja az aktuális állapotot és az akciót, visszaadja az új állapotot — egy tiszta függvény mellékhatások nélkül.
A useReducer különösen akkor hasznos, ha több akció (set, reset, increment, addItem) kezel egy állapotot. Több useState helyett különböző setterekkel — egy reducer tiszta akciótípusokkal. Ez kiszámíthatóvá és könnyen tesztelhetővé teszi a logikát.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'setStep':
return { ...state, step: action.step };
case 'reset':
return { count: 0, step: 1 };
default:
throw new Error('Unknown action: ' + action.type);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
step: 1
});
return (
<>
<p>Count: {state.count}</p>
<button
onClick={() => dispatch({ type: 'increment' })}>+</button>
<button
onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</>
);
}
useRef létrehoz egy változtatható objektumot, amely megmarad a renderek között. A current módosítása nem okoz újrarenderelést. Fő alkalmazás — hozzáférés DOM elemekhez és változtatható értékek tárolása (időzítők, előző értékek, könyvtárpéldányok).
useMemo memóizálja egy függvény eredményét. Csak akkor számítja újra az értéket, ha az egyik függőség megváltozik. Használja költséges számításokhoz — nagy tömbök szűrése, rendezés, adatok formázása. useCallback — a useMemo speciális esete függvények memóizálásához.
import React, { useRef, useMemo, useCallback } from 'react';
function SearchPage({ items, query, onItemSelect }) {
const inputRef = useRef(null);
// Autofókusz mountoláskor
useEffect(() => {
inputRef.current?.focus();
}, []);
// Szűrt eredmény memóizálása
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase()
.includes(query.toLowerCase())
);
}, [items, query]);
// Callback memóizálása
const handleSelect = useCallback((id) => {
onItemSelect(id);
}, [onItemSelect]);
return (
<div>
<input ref={inputRef}
value={query}
onChange={e => setQuery(e.target.value)}
/>
<ul>
{filtered.map(item =>
<li key={item.id}>
<button
onClick={() => handleSelect(item.id)}>
{item.name}
</button>
</li>
)}
</ul>
</div>
);
}
Fontos szabály: a useRef, useMemo és useCallback — optimalizálási eszközök, nem kötelező szerkezetek. Ne csomagoljon be mindent useMemo-ba — először mérje meg a teljesítményt. Az idő előtti optimalizálás bonyolítja a kódot. A React gyorsan rendereli a komponenseket; a useMemo csak akkor szükséges, ha a számítások valóban költségesek (O(n²) és magasabb).
A React két szigorú szabályt vezetett be a hookok számára. Első: a hookokat csak a legfelső szinten hívja — nem feltételekben, ciklusokban vagy beágyazott függvényekben. Második: a hookokat csak React funkcionális komponensekből vagy egyéni hookokból hívja. Az ESLint bővítmény eslint-plugin-react-hooks automatikusan ellenőrzi mindkét szabályt.
A leggyakoribb hiba — a hookok hívási sorrendjének megsértése. A React a hookok hívási sorrendjére támaszkodik a renderek között. Ha egy hook egy feltételben található és nem hívódott meg az egyik renderben, az összes későbbi hook eltolódik, hibákat okozva.
| Hiba | Mi történik | Megoldás |
|---|---|---|
| Hook feltételben | A hookok sorrendje megtörik | Helyezze át a feltétellogikát a hook belsejébe |
| Stale closure | Az effekt régi értéket használ | Adja hozzá az összes függőséget a tömbhöz |
| Végtelen ciklus | A useEffect függőségek nélkül módosítja a state-et | Adja meg a függőségeket vagy használjon useReducer-t |
| Ref módosítása renderben | Mellékhatás a függvény törzsében | Helyezze át a mutációkat a useEffect-be |
A második gyakori hiba — stale closure a useEffect-ben. Ha az effektben olyan változót használnak, amely nincs megadva a függőségekben, az effekt ‘elkapja’ annak értékét a lezárás létrehozásának pillanatában. Megoldás — mindig vegye fel az összes használt változót a függőségi tömbbe. Ha túl sok a függőség, ossza fel az effektet több részre.
Gyakran Ismételt Kérdések
A React a hookok hívási sorrendjére támaszkodik a renderek között. Ha egy hookot feltételben hívtak, és az nem futott le az egyik renderben, az összes későbbi hook ‘elcsúszik’ — az állapotuk összekeveredik. Az ESLint szabály hooks/exhaustive-deps figyelmeztet erre.
useLayoutEffect szinkron és a böngésző általi rajzolás előtt fut le. useEffect aszinkron — a rajzolás után. Használja a useLayoutEffect-et csak akkor, ha a DOM-ot a felhasználó általi látás előtt kell mérni vagy módosítani (popover pozíció, scroll helyreállítás).
Használjon AbortController-t a useEffect-en belül. Hozzon létre egy controllert, adja át a signal-t a fetch-nek, és a cleanup függvényben hívja meg a controller.abort()-ot. Régebbi böngészőkhöz elegendő egy cancelled zászló: return () => { cancelled = true; } ellenőrzéssel a setState előtt.
Egyéni hook — egy JavaScript függvény use előtaggal, amely beépített hookokat használ. Akkor hozza létre, ha ugyanaz a logika hookokkal két vagy több komponensben ismétlődik. Példák: useDebounce, useLocalStorage, useMediaQuery. Az egyéni hookok helyettesítik a HOC-ot és a render props-ot.
Strict Mode-ban a React szándékosan kétszer mountolja és unmountolja a komponenst fejlesztési módban, hogy megtalálja a nem tisztított effektusokkal kapcsolatos hibákat. Ha a cleanup helyesen van megírva, a dupla hívás nem okoz problémát. Éles módban ez nem történik meg.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is