Sådan bruger du useEffect og useState korrekt i React

Sidste ændring: 02/12/2026
Forfatter: C SourceTrail
  • Forstå, hvordan useState bevarer og opdaterer den lokale komponenttilstand, herunder funktionelle opdateringer og objekthåndtering.
  • Brug useEffect til bivirkninger med klar opsætnings-/oprydningslogik og præcise afhængighedsarrays for at undgå lækager og løkker.
  • Kombiner useState og useEffect til virkelige opgaver som datahentning, abonnementer og DOM-opdateringer i funktionskomponenter.
  • Følg reglerne for hooks og behandl effekter som "efter-rendering"-processer for at holde React-komponenter forudsigelige og vedligeholdelsesvenlige.

React hooks useState og useEffect

React hooks har fuldstændig ændret den måde, vi skriver komponenter på, og mestring useState og useEffect er dybest set adgangsbilletten til at skrive moderne React-kode. Hvis du allerede bruger dem, men stadig sidder fast med uendelige løkker, forældet tilstand eller forvirrende afhængighedsarrays, vil denne guide hjælpe dig med at forbinde alle de manglende brikker på en praktisk måde.

I denne artikel vil vi gå i dybden med, hvordan man bruger det korrekt useState og useEffect sammen, hvorfor hooks overhovedet blev introduceret, de officielle regler og forbehold, hvordan afhængigheder rent faktisk fungerer under motorhjelmen, almindelige faldgruber, der ødelægger dine komponenter, og gennemprøvede mønstre for bivirkninger, oprydning og tilstandsstyring i virkelige projekter.

Hvorfor hooks, og hvorfor specifikt useState og useEffect?

Hooks blev tilføjet i React 16.8 for at lade funktionskomponenter bruge tilstands- og livscyklusfunktioner uden klasser.Før det skulle du skrive klassekomponenter for at bevare den lokale tilstand, abonnere på eksterne data eller reagere på livscyklushændelser som montering og afmontering.

Det store problem med klasser var, at relateret logik ofte var opdelt på tværs af flere livscyklusmetoder som componentDidMount, componentDidUpdate og componentWillUnmountDu ville ende med dele af den samme funktion spredt rundt om forskellige metoder baseret på hvornår de løber i stedet for det det gør de, hvilket gør kode sværere at læse, teste og genbruge.

Kroge vender denne model rundt: med useState du knytter tilstand direkte til en funktionskomponent, og med useEffect Du knytter bivirkninger direkte til den logik, der har brug for dem. På den måde kan du gruppere alt relateret til en enkelt bekymring på ét sted og nemt udtrække genbrugelige hooks senere.

Blandt alle kroge, useState og useEffect er de centrale primitiverDu kan bygge de fleste hverdagsfunktioner med blot disse to: UI-tilstand som formularer og til/fra-knapper, netværksanmodninger, abonnementer, timere, DOM-opdateringer og mere. Andre hooks (useRef, useReducer, useContext, useMemo...) er fantastiske, men de bygger oven på de samme idéer.

React-regler for hooks, som du aldrig må bryde

React hooks kommer med et par strenge regler, der får dem til at fungere pålideligt på tværs af gengivelser.Hvis du overtræder dem, vil du enten se runtime-fejl eller meget subtile, svært tilgængelige fejl.

Første regel: kald kun hooks i React-funktionskomponenter eller brugerdefinerede hooksDu kan ikke bruge useState or useEffect i klassekomponenter, almindelige hjælpefunktioner eller uden for nogen komponent. Et mønster som dette er ugyldigt:

import React, { Component, useState } from 'react';

class App extends Component {
  // ❌ This will throw - hooks don’t work in classes
  const  = useState(0);
  render() {
    return <h1>Hello, I am a Class Component!</h1>;
  }
}

Den korrekte fremgangsmåde er at flytte til en funktionskomponent, hvis du vil bruge hooks.:

import React, { useState } from 'react';

function App() {
  const  = useState('');

  return (
    <div>
      Your JSX code goes in here...
    </div>
  );
}

export default App;

Anden regel: kald kun hooks på det øverste niveau af din komponentDet betyder ingen hooks i loops, betingelser eller indbyggede funktioner. React er afhængig af at kalde hooks i samme rækkefølge på hver gengivelse for at "matche" hinanden. useState og useEffect kald med sine lagrede data, så dette er ugyldigt:

function BadComponent({ enabled }) {
  if (enabled) {
    // ❌ Wrong: hook inside a conditional
    const  = useState(0);
  }
  // ...
}

Deklarer i stedet hooks ubetinget øverst og brug betingede elementer inde i effekten eller JSXHook'en skal altid kaldes, men den logik, den kører, kan være betinget:

function ConditionalEffectComponent() {
  const  = useState(false);

  useEffect(() => {
    if (isMounted) {
      console.log('Component mounted');
    }
  }, );

  return (
    <div>
      <button onClick={() => setIsMounted(!isMounted)}>
        {isMounted ? 'Unmount' : 'Mount'}
      </button>
    </div>
  );
}

Den tredje implicitte regel er, at hooks skal importeres fra React (eller et hook-bibliotek), ikke implementeres ad hoc.Dette er indlysende, men værd at sige: magien ligger i Reacts interne hook-dispatcher, der sporer hook-kald på tværs af renders.

Korrekt håndtering af lokal tilstand med useState

useState lader dig knytte tilstand til en funktionskomponent og modtage både den aktuelle værdi og en opdateringsfunktionKonceptuelt er det den funktionelle modstykke til this.state og this.setState i klassekomponenter.

Et minimalt modeksempel med useState ser sådan ud:

import React, { useState } from 'react';

function Counter() {
  const  = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

export default Counter;

Når du ringer useState(initialValue), React gemmer den tilstand og returnerer et par: den aktuelle tilstandsværdi og en setter. I modsætning til normale lokale variabler overlever tilstanden på tværs af gengivelser, så count Værdien nulstilles ikke til 0, hver gang komponentfunktionen kører.

Du kan bruge enhver serialiserbar værdi for tilstand: tal, strenge, boolske værdier, arrays, objekter og endda funktionerDu kan også ringe useState flere gange i den samme komponent for at holde relaterede værdier adskilte i stedet for at samle alt i et enkelt objekt.

Når den nye tilstandsværdi afhænger af den forrige, skal du altid bruge den funktionelle opdateringsformularDette undgår fejl, når flere tilstandsopdateringer sker hurtigt efter hinanden:

setCount(prev => prev + 1);

En anden subtil, men vigtig detalje er, at kald af setteren erstatter hele tilstandsværdien, den fletter ikke objekter som this.setState i klasserHvis din tilstand er et objekt eller et array, skal du selv sprede den forrige værdi:

const  = useState({ name: 'Alex', age: 30 });

// ✅ Correct: copy and update
setUser(prev => ({ ...prev, age: prev.age + 1 }));

For dyre startværdier kan du lazy-initialisere tilstanden ved at sende en funktion til useStateReact kalder det kun på den første gengivelse:

const  = useState(() => calculateInitialValue());

Håndtering af bivirkninger med useEffect

useEffect er Reacts API til at køre sideeffekter i funktionskomponenterEn "bivirkning" er alt, der berører omverdenen: datahentning, logning, direkte DOM-ændringer, abonnementer, timere, browser-API'er osv.

Begrebsmæssigt useEffect erstatter en kombination af componentDidMount, componentDidUpdate og componentWillUnmount fra klassekomponenterI stedet for at opdele én effekt på tværs af tre livscyklusmetoder, deklarerer du den én gang og lader React håndtere, hvornår den kører, og hvornår den rydder op.

Den grundlæggende signatur er useEffect(setup, dependencies?). Det setup `function` er din effektdel; den kan eventuelt returnere en oprydningsfunktion. dependencies Arrayet fortæller React, hvornår effekten skal køres igen.

useEffect(() => {
  // side effect logic here

  return () => {
    // optional cleanup logic here
  };
}, );

Som standard, uden det andet argument, vil effekten køre efter hver gengivelse (første montering og hver efterfølgende opdatering). Det er ofte for meget til netværksanmodninger eller dyr logik.

Et meget almindeligt mønster er at opdatere noget eksternt, når en del af en tilstand ændrer sig.For eksempel opdatering af sidetitlen afhængigt af antallet af klik:

import React, { useState, useEffect } from 'react';

function Counter() {
  const  = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, ); // effect re-runs only when `count` changes

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase</button>
    </div>
  );
}

Afhængighedsarrayet er afgørende for ydeevne og korrekthedDen styrer, hvornår React skal køre effekten igen: hvis en afhængighed er ændret i henhold til Object.is Til sammenligning renses effekten op og kører igen; hvis ingen ændringer foretages, springes den over.

Forstå afhængighedsarrayet som en professionel

Afhængighedsarrayet er der, hvor det er mest subtilt useEffect insekter kommer fraReact sammenligner hvert element i arrayet med dets forrige værdi ved hjælp af Object.isHvis alle værdier er ens, springes effekten over; hvis mindst én er forskellig, udføres effekten igen.

Der er tre primære afhængighedskonfigurationer, du vil bruge hele tiden:

  • Intet andet argumentEffekten kører efter hver gengivelse.
  • Tomt array []Effekten kører kun én gang ved montering og rydder op ved afmontering.
  • Array med værdier Effekten kører efter montering og når en afhængighed ændres.

Når afhængigheder er primitive værdier (tal, strenge, boolske værdier), er dette ligetil.Problemer starter, når du placerer objekter, arrays eller funktioner inden for afhængighederne, fordi lighed er referencebaseret. To identiske objekter med forskellige referencer betragtes som "forskellige", hvilket forårsager gentagelser ved hver gengivelse.

Overvej en effekt, der afhænger af en team genstand fra rekvisitter:

function Team({ team }) {
  useEffect(() => {
    console.log(team.id, team.active);
  }, ); // ⚠️ might re-run every render if `team` reference changes
}

Selv hvis det faktiske holdindhold ikke ændres, vil en ny objektreference på hver gengivelse tvinge effekten til at køre igen.For at undgå dette skal du enten stole på de primitive felter, du rent faktisk bruger, eller rekonstruere objektet inde i selve effekten.

En mere sikker version sporer kun, hvad effekten virkelig har brug for:

function Team({ team }) {
  const { id, active } = team;

  useEffect(() => {
    console.log(id, active);
  }, );
}

Hvis du virkelig har brug for hele objektet inde i effekten, kan du genskabe det der i stedet for at bruge det som en afhængighedPå den måde kan afhængighedslisten stadig være baseret på primitiver:

function Team({ team }) {
  const { id, active, name } = team;

  useEffect(() => {
    const localTeam = { id, active, name };
    // use `localTeam` here
  }, );
}

Som en sidste udvej kan du bruge memoisering med useMemo or useCallback til dyre genstande eller funktioner, men husk at memo-funktionalitet i sig selv har en pris. Drys det ikke overalt "bare i tilfælde af"; tilføj det, når en specifik afhængighed virkelig forårsager ydeevneproblemer.

Korrekt rengøring af effekter

Nogle bivirkninger allokerer ressourcer, der skal frigives: abonnementer, sockets, intervaller, timeouts, eventlistenere osv. Hvis man glemmer at rydde op i dem, kan det nemt føre til hukommelseslækager eller duplikeret arbejde.

In useEffect, oprydning håndteres ved at returnere en funktion fra effektenReact vil kalde denne funktion, før effekten køres igen med nye afhængigheder, og også en sidste gang, når komponenten afmonteres.

import { useEffect } from 'react';

function LogMessage({ message }) {
  useEffect(() => {
    const log = setInterval(() => {
      console.log(message);
    }, 1000);

    return () => {
      clearInterval(log);
    };
  }, );

  return <div>logging to console "{message}"</div>;
}

I dette eksempel, hver gang message ændringer, React rydder først det gamle interval og opretter derefter et nyt med den opdaterede beskedNår komponenten forsvinder fra brugergrænsefladen, rydder den sidste oprydning intervallet permanent.

Denne parring af "opsætning + oprydning" er central for den mentale model af useEffectPrøv at tænke på hver effekt som en selvstændig proces, der starter i opsætningsfunktionen og stopper helt i oprydningsfunktionen. React kan køre flere opsætnings-/oprydningscyklusser under udvikling (især i Strict Mode) for at stressteste, om din oprydning virkelig fortryder alt.

Et klassisk eksempel er at abonnere på en ekstern kilde, f.eks. en chat-API eller browserhændelse (se håndtering af onKeyDown i React):

useEffect(() => {
  function handleClick(event) {
    console.log('Clicked', event.clientX, event.clientY);
  }

  document.addEventListener('click', handleClick);

  return () => {
    document.removeEventListener('click', handleClick);
  };
}, []); // runs once on mount, cleans up on unmount

Brug af useState og useEffect sammen til datahentning

En af de mest almindelige kombinationer i den virkelige verden er at bruge useState og useEffect at hente data fra et APIDu holder dataene (og måske indlæsnings-/fejlflag) i deres tilstand og udfører anmodningen i en effekt, der kører, når komponenten monteres, eller når en parameter ændres.

Et grundlæggende mønster til at hente data ved montering ser sådan ud:

import { useEffect, useState } from 'react';

function FetchItems() {
  const  = useState([]);

  useEffect(() => {
    let ignore = false;

    async function fetchItems() {
      try {
        const response = await fetch('/items');
        const fetchedItems = await response.json();
        if (!ignore) {
          setItems(fetchedItems);
        }
      } catch (error) {
        console.error('Error fetching items:', error);
      }
    }

    fetchItems();

    return () => {
      // avoid updating state if the component unmounted
      ignore = true;
    };
  }, []);

  return (
    <div>
      {items.map(item => (
        <div key={item.id ?? item}>{item.name ?? item}</div>
      ))}
    </div>
  );
}

Her sikrer det tomme afhængighedsarray, at anmodningen kører præcis én gangDet interne ignore Et flag er en simpel måde at undgå at indstille en tilstand på en ikke-monteret komponent, hvis anmodningen løses sent.

Det er også meget almindeligt at tilføje et indlæsningsflag og vise en spinner eller pladsholder, mens data er undervejs.:

const Statistics = () => {
  const  = useState([]);
  const  = useState(true);

  useEffect(() => {
    const getStats = async () => {
      try {
        const statsData = await getData();
        setStats(statsData);
      } finally {
        setLoading(false);
      }
    };

    getStats();
  }, []);

  if (loading) {
    return <div>Loading statistics...</div>;
  }

  return (
    <ul>
      {stats.map(stat => (
        <li key={stat.id}>{stat.label}: {stat.value}</li>
      ))}
    </ul>
  );
};

Hvis din forespørgsel afhænger af en parameter (f.eks. en kategori, et filter eller en ruteparameter), skal du føje den parameter til afhængighedsarrayet. så effekten gentages, når den ændres:

useEffect(() => {
  async function fetchItems() {
    const response = await fetch(`/items?category=${category}`);
    const data = await response.json();
    setItems(data);
  }

  fetchItems();
}, );

Tænkning i "effekter på hver gengivelse" kontra "livscyklusser"

Hvis du er vant til klassekomponenter, kan det være fristende at lave en mental planlægning useEffect at montere/opdatere/afmontere metoder, men det fører normalt til mere forvirring. En enklere mental model er: "effekter kører efter gengivelser og rydder muligvis op inden næste kørsel".

I timerne skulle man ofte duplikere logik mellem componentDidMount og componentDidUpdate fordi du ønskede, at den samme effekt skulle køre både ved mount og ved opdateringer. Med hooks forsvinder den duplikering: en enkelt effekt dækker begge tilfælde, og React tager sig af at rydde op mellem kørsler.

Dette design eliminerer også en hel klasse af fejl omkring ikke at håndtere opdateringer korrektFor eksempel, i en klassekomponent, der abonnerer på en vens onlinestatus, er det nemt at glemme at abonnere igen, når props.friend ændringer, hvilket forårsager forældede abonnementer eller nedbrud ved afmontering. Med useEffect der lister friend.id Som en afhængighed vil React automatisk køre oprydningen for den gamle ven og opsætningen for den nye.

Husk dog på, at React i Strict Mode bevidst kører din opsætnings- og oprydningscyklus to gange ved montering.Dette sker ikke i produktion, men det er en nyttig stresstest til at bekræfte, at din oprydning virkelig fortryder alt, og at din effekt sikkert kan køres flere gange.

Optimering og fejlfinding af useEffect-adfærd

Når en effekt kører oftere end forventet, er det første, du skal kontrollere, afhængighedsmatrixenEnten ændres en afhængighed ved hver gengivelse (almindeligt med inline-objekter/funktioner), eller også har du glemt at angive arrayet overhovedet.

Logføring af afhængighedsværdierne er en hurtig måde at foretage fejlfinding på:

useEffect(() => {
  console.log('Effect deps:', dep1, dep2);
}, );

Hvis du ser forskellige logfiler hver gang, skal du kontrollere, hvilken afhængighed der rent faktisk ændrer sigOfte vil du se et indlejret objekt eller en pilfunktion blive genskabt ved hver gengivelse. Objektoprettelsen flyttes inden for effekten, funktioner løftes uden for komponenten eller gemmes med useCallback kan stabilisere afhængigheder, når det er nødvendigt.

Uendelige løkker opstår, når en effekt både afhænger af en værdi og ubetinget opdaterer den samme værdi.. For eksempel:

useEffect(() => {
  setCount(count + 1); // ⚠️ will cause a loop if `count` is a dependency
}, );

Hver gang count ændringer, effekten kører, opdateringer count igen, udløser en anden gengivelse, og så videreFor at bryde dette mønster skal du overveje, om tilstandsopdateringen virkelig hører hjemme i en effekt, om den i stedet skal udløses af en brugerinteraktion, eller om du kan stole på en anden værdi.

Nogle gange vil du gerne læse den seneste værdi af en tilstand eller rekvisitter i en effekt uden at den værdi udløser en gentagelse.I disse avancerede scenarier er nyere API'er som "effekthændelser" (via useEffectEvent i React-dokumentationen) eller referencer kan hjælpe, men i de fleste praktiske tilfælde er det sikrere og enklere at forblive tro mod afhængigheder.

At sætte alt sammen ved hjælp af useState og useEffect koger korrekt ned til et par kernevanerHold tilstanden lille og fokuseret, foretræk funktionelle opdateringer, når du udleder nye tilstande fra gamle, strukturer effekter omkring opsætnings-/oprydningspar, vær ærlig og eksplicit med afhængighedsarrays, og respekter altid reglerne for hooks, så React pålideligt kan spore, hvad der hører til hvor. Når du følger disse principper, forbliver dine komponenter forudsigelige, dine sideeffekter opfører sig korrekt, og din React-kodebase bliver langt lettere at udvikle, efterhånden som din app vokser.

relateret artikel:
Løst: Sådan installeres reagere native kroge med
Relaterede indlæg: