React Hooks — är funktioner som gör att du kan använda tillstånd, effekter, kontext och andra React-funktioner i funktionella komponenter, utan att skriva klasser. Introducerade i React 16.8 har hooks radikalt förändrat utvecklingsmetoden genom att ersätta komplexa mönster som HOC och render props. Enligt React, 2024 finns det 15 inbyggda hooks som täcker alla scenarier för komponenthantering.
Huvudsakligt
React Hooks — en uppsättning inbyggda funktioner i React som gör att du kan ‘ansluta’ till ramverkets interna mekanismer utan att skapa klasser. Före React 16.8 kunde funktionella komponenter endast ta emot props och returnera JSX — all komplex logik krävde en klasskomponent.
Problemet med klasskomponenter — separation av relaterad logik. Kod som tillhörde en enhet (till exempel en WebSocket-prenumeration) tvingades delas mellan componentDidMount, componentDidUpdate och componentWillUnmount. Hooks löser detta: all logik grupperas i en useEffect.
Det andra problemet — återanvändning av logik. För återanvändning av tillståndslogik mellan klasskomponenter var man tvungen att tillämpa HOC (högre ordningens komponenter) eller render props — båda mönstren skapade extra nästling i komponentträdet. Anpassade hooks löser detta problem utan nästling.
useState — den mest grundläggande hooken. Den accepterar ett initialvärde och returnerar en tuple: det aktuella värdet och en funktion för att uppdatera det. Till skillnad från this.setState i klasser slår useState inte samman objekt — det nya tillståndet ersätter helt det föregående för ett specifikt anrop till useState.
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>
);
}
Setter för useState kan acceptera en funktion som tar emot det föregående värdet och returnerar ett nytt. Funktionell form är obligatorisk när det nya tillståndet beräknas från det föregående och batchade setState-anrop är möjliga — varje anrop får det aktuella föregående värdet.
useEffect — hook för att utföra sidoeffekter i funktionella komponenter. Den accepterar två argument: en effektfunktion och en beroendematris. React kör effekten efter varje render, men när beroenden anges — endast när något av dem har ändrats.
En tom beroendematris [] innebär att effekten körs en gång efter den första rendern (analogt med componentDidMount). Att returnera en funktion från effekten är cleanup, som körs vid avmontering (analogt med componentWillUnmount) och före återkörning av effekten.
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 - stäng socket
return () => socket.close();
}, [roomId]);
return <ul>
{messages.map((msg, i) =>
<li key={i}>{msg.text}</li>
)}
</ul>;
}
Beroenden för useEffect — är den enda kopplingen mellan effekten och omvärlden. Om en variabel från props eller tillstånd används i effekten måste den finnas i beroendematrisen. Att ignorera denna regel är källan till buggar med inaktuell data (stale closure).
useContext — hook för att läsa kontextvärdet. Den accepterar kontextobjektet som skapats med React.createContext och returnerar det aktuella värdet. Om kontexten ändras, renderas komponenten automatiskt om med det nya värdet.
Innan useContext kom, krävde åtkomst till kontext i funktionella komponenter en omslag i Context.Consumer, vilket skapade nästling. useContext eliminerar helt Consumer — det räcker att anropa hooken var som helst i den funktionella komponenten.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('ljus');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button
className={`btn btn-${theme}`}
>
{theme === 'dark' ? 'moon' : '☀️'}
</button>;
}
useContext är inte en ersättning för Redux eller globalt tillstånd. React rekommenderar att använda kontext för sällan föränderliga värden — tema, språk, auktorisering. För ofta uppdaterad data kommer varje omrendering att beräkna om alla konsumenter av kontexten, vilket kan minska prestandan.
useReducer — alternativ till useState för tillstånd med komplex uppdateringslogik. Den accepterar en reducer-funktion och ett initialtillstånd, returnerar det aktuella tillståndet och en dispatch-funktion. Reducer tar emot aktuellt tillstånd och handling, returnerar ett nytt tillstånd — en ren funktion utan sidoeffekter.
useReducer är särskilt användbart när flera handlingar (set, reset, increment, addItem) hanterar ett tillstånd. Istället för flera useState med olika setters — en reducer med tydliga handlingstyper. Detta gör logiken förutsägbar och lätt att testa.
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 skapar ett föränderligt objekt som bevaras mellan renders. Ändring av current orsakar inte omrendering. Huvudtillämpning — åtkomst till DOM-element och lagring av muterbara värden (timers, tidigare värden, biblioteksinstanser).
useMemo memoizerar resultatet av en funktion. Den räknar om värdet endast när ett av beroendena ändras. Använd för dyra beräkningar — filtrering av stora arrayer, sortering, formatering av data. useCallback — ett specialfall av useMemo för memoizering av funktioner.
import React, { useRef, useMemo, useCallback } from 'react';
function SearchPage({ items, query, onItemSelect }) {
const inputRef = useRef(null);
// Autofokus vid montering
useEffect(() => {
inputRef.current?.focus();
}, []);
// Memorera filtrerat resultat
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase()
.includes(query.toLowerCase())
);
}, [items, query]);
// Memorera callback
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>
);
}
Viktig regel: useRef, useMemo och useCallback — optimeringsverktyg, inte obligatoriska konstruktioner. Slå inte in allt i useMemo — mät först prestandan. För tidig optimering komplicerar koden. React renderar komponenter snabbt; useMemo behövs bara när beräkningar verkligen är dyra (O(n²) och högre).
React införde två strikta regler för hooks. Första: anropa hooks endast på högsta nivån — inte inuti villkor, loopar eller nästlade funktioner. Andra: anropa hooks endast från React funktionella komponenter eller anpassade hooks. ESLint-plugin eslint-plugin-react-hooks kontrollerar automatiskt båda reglerna.
Det vanligaste misstaget — brott mot anropsordningen för hooks. React förlitar sig på anropsordningen för hooks mellan renders. Om en hook finns inuti ett villkor och inte anropades i en av renders, flyttas alla efterföljande hooks, vilket orsakar buggar.
| Misstag | Vad händer | Lösning |
|---|---|---|
| Hook i villkor | Hook-ordningen bryts | Flytta villkorslogiken inuti hooken |
| Stale closure | Effekten använder gammalt värde | Lägg till alla beroenden i matrisen |
| Oändlig loop | useEffect utan beroenden ändrar tillstånd | Ange beroenden eller använd useReducer |
| Ändra ref i render | Sidoeffekt i funktionskroppen | Flytta mutationer till useEffect |
Det andra vanliga misstaget — stale closure i useEffect. Om en variabel som inte anges i beroendena används i effekten, ‘fångar’ effekten dess värde vid skapandet av slutningen. Lösning — inkludera alltid alla använda variabler i beroendematrisen. Om det finns för många beroenden, dela upp effekten i flera delar.
Vanliga frågor
React förlitar sig på anropsordningen för hooks mellan renders. Om en hook anropades i ett villkor och inte fungerade i en av renders, ‘glider’ alla efterföljande hooks — deras tillstånd blandas. ESLint-regeln hooks/exhaustive-deps varnar för detta.
useLayoutEffect är synkron och körs innan webbläsaren ritar ändringarna. useEffect är asynkron — efter ritning. Använd useLayoutEffect endast när du behöver mäta eller ändra DOM innan användaren ser ändringarna (popover-position, återställning av scroll).
Använd AbortController inuti useEffect. Skapa en controller, skicka signal till fetch och i cleanup-funktionen anropa controller.abort(). För äldre webbläsare räcker en cancelled-flagga: return () => { cancelled = true; } med kontroll före setState.
Anpassad hook — en JavaScript-funktion med prefixet use som använder inbyggda hooks. Skapa den när samma logik med hooks upprepas i två eller fler komponenter. Exempel: useDebounce, useLocalStorage, useMediaQuery. Anpassade hooks ersätter HOC och render props.
I Strict Mode monterar och avmonterar React avsiktligt komponenten två gånger i utvecklingsläge för att hitta fel med orensade effekter. Om cleanup är korrekt skriven orsakar inte dubbelanropet problem. I produktionsläge händer detta inte.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också