useCallback — är en React-hook som returnerar en memoiserad version av funktionen, som inte ändras mellan renderningar så länge dess beroenden inte ändras. Till skillnad från vanlig deklaration av en funktion inuti komponenten (som skapar en ny funktion vid varje render) stabiliserar useCallback referensen till funktionen, vilket förhindrar onödiga re-renders av barnkomponenter som optimerats genom React.memo. Enligt React Documentation (2025) är useCallback endast användbar i kombination med React.memo eller hooks som beror på en stabil referens.
Det viktigaste
useCallback — är en hook som lades till i React 16.8 och som memoiserar funktionen: den returnerar samma referens tills beroendena ändras. Utan useCallback skapar varje deklaration av en funktion inuti komponenten ett nytt funktionsobjekt vid varje render. För primitiva värden är detta omärkbart, men när sådana callbacks skickas till barnkomponenter som optimerats genom React.memo orsakar varje ny referens en re-render av barnkomponenten.
Syntaxmässigt är useCallback ekvivalent med useMemo för funktioner: useCallback(fn, deps) — är en förkortning för useMemo(() => fn, deps). React lagrar den memoiserade funktionen i den interna lagringen av fiber-noden och jämför beroendena vid varje render. Om beroendena inte har ändrats (Object.is för varje element) returneras den tidigare funktionen.
Enligt React Documentation — useCallback (2025) behöver du inte linda in varje funktion i useCallback. Hooken har sitt pris: anrop av hooken, jämförelse av beroenden och minnesallokering för beroendearrayen. Om komponenten är enkel och inte har djupa träd med React.memo kommer useCallback bara att sakta ner applikationen. Optimering måste vara mätbar, inte intuitiv.
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// Stabil referens — samma funktion tills deps ändras
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return <Child onClick={handleClick} />;
}
Memoization i useCallback bygger på cachelagring av resultatet av funktionsanropet. React bevarar den closure som skapades vid den första rendern och returnerar den vid efterföljande renderningar så länge beroendena förblir oförändrade. Inuti fiber-noden skapar varje useCallback-anrop en nod i hookarnas länkade lista, där tidigare beroenden och det memoiserade värdet lagras.
Processen för att jämföra beroenden sker strikt genom Object.is — det är en ytlig jämförelse, utan djup kontroll av objekt eller arrayer. Om det finns ett objekt eller en array i beroendena kommer den nya referensen vid varje render att betraktas som en ändring. Därför måste du i beroendearrayen skicka primitiva värden eller stabila referenser (till exempel från useRef eller useMemo).
Enligt React Core Team — Optimization Guide (2024) omfattar kostnaden för memoization tre komponenter: allokering av beroendearrayen vid varje render, genomgång och jämförelse av elementen genom Object.is samt potentiell overhead från garbage collection vid återskapande. För en komponent med hundratals useCallback-inpackningar kan detta bli märkbart — därför är selektivitet i användningen av hooken avgörande.
| Scenario | Utan useCallback | Med useCallback |
|---|---|---|
| Skapande av funktion | Ny vid varje render | Samma vid stabila deps |
| Överföring till React.memo | Barnet re-rendrar | Barnet re-rendrar inte |
| I useEffect-arrayen | Effekten startas om | Effekten är stabil |
| Overhead | Minimal | Jämförelse av beroenden + minne |
Det finns en utbredd myt om att useCallback automatiskt förbättrar prestandan. I verkligheten saktar useCallback i isolering (utan React.memo) till och med ner applikationen något på grund av kostnaden för att jämföra beroenden. Hooken ger verklig nytta endast i tre scenarier: förhindrande av re-renders av React.memo-komponenter, stabilitet för callbacks i useEffect och överföring till custom hooks som beror på reference equality.
Regeln är enkel: tills du har hittat ett prestandaproblem genom React DevTools Profiler — använd inte useCallback. React-teamet har upprepade gånger betonat att för tidig optimering är roten till allt ont. Skriv först ren kod utan memoization, mät, hitta flaskhalsen i profileraren och lägg först därefter till useCallback där det verkligen behövs.
// Mätbar optimering: Child är inlindad i React.memo
const Child = React.memo(({ onClick }) => {
console.log('Child re-rendrade');
return <button onClick={onClick}>Click</button>;
});
function Parent() {
const handleClick = useCallback(() => {
console.log('klickat');
}, []);
return <Child onClick={handleClick} />;
}
Enligt Dan Abramov — Before You memo() (2024) är över 90 % av användningarna av useCallback i open source-projekt överflödiga. Utvecklare lindar in varje funktion "för säkerhets skull" utan att mäta effekten. Alternativ: om barnkomponenten är tung och dess re-render är dyr — är React.memo + useCallback motiverade. Om barnkomponenten är lätt — är en re-render billigare än att jämföra beroenden.
Första scenariot — React.memo. Om barnkomponenten är inlindad i React.memo och tar emot en callback-funktion som prop, kommer barnkomponenten utan useCallback att ritas om vid varje render av föräldern, även om dess egna data inte har ändrats. useCallback stabiliserar referensen och React.memo kan korrekt hoppa över re-rendern.
Andra scenariot — useEffect med callback i beroendena. Om funktionen skickas till useEffects beroendearray kommer varje ny referens att starta om effekten. useCallback garanterar att referensen är stabil och att effekten endast körs när de faktiska data ändras, inte vid varje render. Detta är särskilt viktigt för prenumerationer och förfrågningar.
// useCallback för stabil useEffect-beroende
const fetchData = useCallback(async (id) => {
const res = await fetch(`/api/${id}`);
setData(res.data);
}, []); // stabil referens, återskapas aldrig
useEffect(() => {
fetchData(props.id);
}, [props.id, fetchData]); // effekten körs endast när props.id ändras
Den främsta skillnaden mellan useCallback och useMemo är vad var och en memoiserar. useCallback memoiserar funktionen: useCallback(fn, deps) returnerar fn (samma eller tidigare version). useMemo memoiserar resultatet av funktionsanropet: useMemo(() => computeExpensive(a, b), [a, b]) returnerar det beräknade värdet, inte funktionen.
Tekniskt sett är useCallback syntaktiskt socker ovanpå useMemo: useCallback(fn, deps) är ekvivalent med useMemo(() => fn, deps). Denna syntax finns endast för läsbarhetens skull — så att utvecklaren tydligt ser att det är just funktionen som memoiseras, inte värdet. Det finns ingen skillnad i prestanda mellan useCallback och useMemo med funktion — de genererar samma kod.
| Hook | Memoiserar | Syntax | Användning |
|---|---|---|---|
| useCallback | Funktionen (referensen) | useCallback(fn, deps) | Callbacks för barnkomponenter |
| useMemo | Resultatet av beräkningen | useMemo(() => value, deps) | Dyra beräkningar, memoization av objekt |
// Dessa är ekvivalenta:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);
Det vanligaste misstaget — att meningslöst linda in alla funktioner i useCallback utan React.memo på barnkomponenter. Om barnkomponenten inte är inlindad i React.memo kommer den ändå att re-rendras vid varje render av föräldern, oavsett om callbackens referens ändras eller inte. useCallback utan React.memo — kostnad utan nytta.
// ❌ Värdelös: utan React.memo på child
const handle = useCallback(() => doStuff(), []);
<Child onClick={handle} />; // Child re-rendrar ändå utan React.memo
// ❌ Stale closure: saknat beroende
const handle = useCallback(() => {
console.log(count); // count är alltid 0 — stale closure!
}, []);
// ✅ Korrekt: inkludera beroenden
const handle = useCallback(() => {
console.log(count);
}, [count]);
Problemet med stale closure i useCallback löses genom att ange alla använda variabler i beroendearrayen. eslint-plugin-react-hooks med exhaustive-deps kontrollerar automatiskt att alla variabler från callback-kroppen finns i arrayen. Om callbacken använder setState, som inte ändras mellan renderningar, kan den säkert anges i deps — React garanterar stabiliteten för setState.
Vanliga frågor
Nej. useCallback är endast meningsfullt i tre fall: barnkomponenten är inlindad i React.memo, funktionen används i useEffects beroendearray eller funktionen skickas till en custom hook som beror på reference equality. I övriga fall tillför useCallback overhead utan nytta. React-teamet rekommenderar att du först skriver utan optimeringar och lägger till dem baserat på resultaten av profilering.
För enkla komponenter är en ny funktion vid varje render något snabbare, eftersom useCallback förbrukar resurser på att jämföra beroenden och allokera arrayer. För komponenter med djupa React.memo-träd vinner useCallback, eftersom det förhindrar re-renders av tusentals element. Mät och jämför, gissa inte — använd React DevTools Profiler för en objektiv bedömning.
Ja, useCallback fungerar med async-funktioner precis som med synkrona. Hooken memoiserar själva funktionen, och resultatet (Promise) returneras varje gång vid anropet. En asynkron funktion inuti useCallback är ett vanligt mönster för stabila callbacks för datahämtning som används i useEffect: const fetchData = useCallback(async (id) => {...}, []).
Använd React DevTools Profiler — den visar vilka komponenter som re-rendras och varför. För programmeringsbaserad kontroll lägg till console.log eller useWhyDidYouUpdate — ett bibliotek som loggar orsaken till re-rendern. De främsta orsakerna: en prop har ändrats (inklusive callback-referensen), state eller kontext har ändrats. Om useCallback inte hjälper — kontrollera att alla beroenden är korrekt angivna.
Även utan React.memo kan useCallback vara användbar i kombination med useMemo för kontextens value. Om du skickar ett objekt med funktioner till Context.Provider, linda in skapandet av objektet i useMemo och varje funktion i useCallback. Detta förhindrar re-renders av alla konsumenter av kontexten när en av funktionerna ändras. Men för direkt överföring av callbacks i props utan React.memo ger useCallback ingen nytta.
Slutsatser
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å