Hur man använder useEffect och useState korrekt i React

Senaste uppdateringen: 02/12/2026
Författare: C SourceTrail
  • Förstå hur useState bevarar och uppdaterar lokala komponenters tillstånd, inklusive funktionella uppdateringar och objekthantering.
  • Använd useEffect för biverkningar med tydlig installations-/rensningslogik och exakta beroendematriser för att undvika läckor och loopar.
  • Kombinera useState och useEffect för verkliga uppgifter som datahämtning, prenumerationer och DOM-uppdateringar i funktionskomponenter.
  • Följ reglerna för hooks och behandla effekter som "efterrendering"-processer för att hålla React-komponenter förutsägbara och underhållbara.

React hooks useState och useEffect

React hooks har helt förändrat hur vi skriver komponenter, och mastering useState och useEffect är i princip inträdesbiljetten till att skriva modern React-kod. Om du redan använder dem men fortfarande fastnar med oändliga loopar, inaktuella tillstånd eller förvirrande beroendematriser, kommer den här guiden att hjälpa dig att koppla ihop alla saknade delar på ett praktiskt sätt.

I den här artikeln går vi djupare in på hur man använder den på rätt sätt useState och useEffect tillsammans, varför hooks introducerades från första början, de officiella reglerna och förbehållen, hur beroenden verkligen fungerar under huven, vanliga fallgropar som orsakar förstörelse av komponenter, och beprövade mönster för biverkningar, rensning och tillståndshantering i verkliga projekt.

Varför hooks, och varför specifikt useState och useEffect?

Hooks lades till i React 16.8 för att låta funktionskomponenter använda tillstånds- och livscykelfunktioner utan klasser.Innan dess var man tvungen att skriva klasskomponenter för att behålla lokalt tillstånd, prenumerera på externa data eller reagera på livscykelhändelser som montering och avmontering.

Det stora problemet med klasser var att relaterad logik ofta var uppdelad på flera livscykelmetoder såsom componentDidMount, componentDidUpdate och componentWillUnmountDu skulle få delar av samma funktion utspridda runt olika metoder baserat på när de springer istället för vad det gör de, vilket gör kod svårare att läsa, testa och återanvända.

Krokar vänder den här modellen: med useState du kopplar tillstånd direkt till en funktionskomponent, och med useEffect du kopplar biverkningar direkt till den logik som behöver dem. På så sätt kan du gruppera allt som rör en enda fråga på ett ställe och enkelt extrahera återanvändbara krokar senare.

Bland alla krokar, useState och useEffect är de centrala primitivernaDu kan bygga de flesta vardagliga funktioner med bara dessa två: UI-tillstånd som formulär och växlar, nätverksförfrågningar, prenumerationer, timers, DOM-uppdateringar och mer. Andra hooks (useRef, useReducer, useContext, useMemo...) är bra, men de bygger vidare på samma idéer.

React-regler som du aldrig får bryta

React hooks kommer med ett par strikta regler som gör att de fungerar tillförlitligt över olika renderings.Om du bryter mot dem kommer du antingen att se körtidsfel eller mycket subtila, svårfelsökta buggar.

Första regeln: anropa hooks endast inuti React-funktionskomponenter eller anpassade hooksDu kan inte använda useState or useEffect i klasskomponenter, vanliga verktygsfunktioner eller utanför någon komponent. Ett mönster som detta är ogiltigt:

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>;
  }
}

Det korrekta tillvägagångssättet är att gå vidare till en funktionskomponent om du vill använda krokar.:

import React, { useState } from 'react';

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

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

export default App;

Andra regeln: anropa endast hooks på den översta nivån av din komponentDet betyder inga hooks inuti loopar, villkor eller kapslade funktioner. React förlitar sig på att anropa hooks i samma ordning på varje rendering för att "matcha ihop" varandra. useState och useEffect anrop med dess lagrade data, så detta är ogiltigt:

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

Deklarera istället hooks villkorslöst högst upp och använd villkor inuti effekten eller JSXHooken måste alltid anropas, men logiken den kör kan vara villkorlig:

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 underförstådda regeln är att hooks måste importeras från React (eller ett hook-bibliotek), inte implementeras ad hoc.Detta är uppenbart, men värt att säga: magin ligger i Reacts interna hook-dispatcher som spårar hook-anrop över renderingar.

Hantera lokalt tillstånd korrekt med useState

useState låter dig koppla tillstånd till en funktionskomponent och ta emot både det aktuella värdet och en uppdateringsfunktionKonceptuellt är det den funktionella motsvarigheten till this.state och this.setState i klasskomponenter.

Ett minimalt motexempel med useState ser ut så här:

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 lagrar det tillståndet och returnerar ett par: det aktuella tillståndsvärdet och en setter. Till skillnad från vanliga lokala variabler överlever tillståndet oavsett rendering, så count värdet återställs inte till 0 varje gång komponentfunktionen körs.

Du kan använda vilket serialiserbart värde som helst för tillstånd: tal, strängar, booleska värden, arrayer, objekt och till och med funktioner.Du kan också ringa useState flera gånger i samma komponent för att hålla relaterade värden separerade istället för att samla allt i ett enda objekt.

När det nya tillståndsvärdet beror på det föregående, använd alltid det funktionella uppdateringsformuläretDetta undviker buggar när flera tillståndsuppdateringar sker i snabb följd:

setCount(prev => prev + 1);

En annan subtil men viktig detalj är att anrop av settern ersätter hela tillståndsvärdet, det slår inte samman objekt som this.setState i klasserOm ditt tillstånd är ett objekt eller en array måste du själv sprida det föregående värdet:

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

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

För dyra initialvärden kan du lazy-initialisera tillståndet genom att skicka en funktion till useStateReact anropar det bara vid den första renderingen:

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

Hantering av biverkningar med useEffect

useEffect är Reacts API för att köra sidoeffekter i funktionskomponenterEn "sidoeffekt" är allt som berör omvärlden: datahämtning, loggning, direkta DOM-ändringar, prenumerationer, timers, webbläsar-API:er etc.

Begreppsmässigt useEffect ersätter en kombination av componentDidMount, componentDidUpdate och componentWillUnmount från klasskomponenterIstället för att dela upp en effekt över tre livscykelmetoder deklarerar du den en gång och låter React hantera när den körs och när den rensar upp.

Den grundläggande signaturen är useEffect(setup, dependencies?). De setup function är din effektkropp; den kan valfritt returnera en rensningsfunktion. dependencies arrayen talar om för React när effekten behöver köras om.

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

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

Som standard, utan det andra argumentet, körs effekten efter varje rendering (första monteringen och varje efterföljande uppdatering). Det är ofta för mycket för nätverksförfrågningar eller dyr logik.

Ett mycket vanligt mönster är att uppdatera något externt närhelst ett tillstånd ändrasTill exempel, uppdatering av sidtiteln beroende på klickantalet:

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>
  );
}

Beroendematrisen är avgörande för prestanda och korrekthetDen styr när React ska köra om effekten: om något beroende har ändrats enligt Object.is I jämförelse rensas effekten upp och körs igen; om ingen ändring görs hoppas den över.

Förstå beroendematrisen som ett proffs

Beroendematrisen är där den är mest subtil useEffect insekter kommer frånReact jämför varje element i arrayen med dess tidigare värde med hjälp av Object.isOm alla värden är lika hoppas effekten över; om minst ett är annorlunda utförs effekten igen.

Det finns tre huvudsakliga beroendekonfigurationer som du kommer att använda hela tiden:

  • Inget andra argumenteffekten körs efter varje rendering.
  • Tom array []Effekten körs bara en gång vid montering och rensas upp vid avmontering.
  • Matris med värden effekten körs efter montering och närhelst ett beroende ändras.

När beroenden är primitiva värden (tal, strängar, booleska värden) är detta enkelt.Problem börjar när du placerar objekt, arrayer eller funktioner inuti beroenden, eftersom likhet är referensbaserad. Två identiska objekt med olika referenser betraktas som "olika", vilket orsakar omkörningar vid varje rendering.

Betrakta en effekt som beror på en team föremål från rekvisita:

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

Även om det faktiska teaminnehållet inte ändras, kommer en ny objektreferens på varje rendering att tvinga effekten att köras igen.För att undvika detta, förlita dig antingen på de primitiva fält du faktiskt använder, eller rekonstruera objektet inuti själva effekten.

En säkrare version spårar bara vad effekten verkligen behöver:

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

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

Om du verkligen behöver hela objektet inuti effekten kan du återskapa det där istället för att använda det som ett beroendePå så sätt kan beroendelistan fortfarande baseras på primitiva värden:

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

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

Som en sista utväg kan du använda memoisering med useMemo or useCallback för dyra föremål eller funktioner, men kom ihåg att memo-arbete i sig har ett pris. Strö det inte överallt "för säkerhets skull"; lägg till det när ett specifikt beroende verkligen orsakar prestandaproblem.

Rengöring av effekter korrekt

Vissa biverkningar allokerar resurser som måste frigöras: prenumerationer, sockets, intervall, timeouts, händelselyssnare etc. Att glömma att rensa upp dem kan lätt leda till minnesläckor eller dubbelarbete.

In useEffect, rensning hanteras genom att returnera en funktion från effektenReact kommer att anropa den här funktionen innan effekten körs igen med nya beroenden, och även en sista gång när komponenten avmonteras.

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 det här exemplet, varje gång message ändringar, React rensar först det gamla intervallet och skapar sedan ett nytt med det uppdaterade meddelandetNär komponenten försvinner från användargränssnittet rensar den senaste rensningen intervallet för gott.

Denna kombination av "uppställning + städning" är central för den mentala modellen av useEffectFörsök att tänka på varje effekt som en fristående process som startar i setup-funktionen och helt stoppar i cleanup-funktionen. React kan köra flera setup-/cleanup-cykler under utveckling (särskilt i Strict Mode) för att stresstesta att din rensning verkligen ångrar allt.

Ett klassiskt exempel är att prenumerera på en extern källa, som ett chatt-API eller en webbläsarhändelse (se hantering av 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

Använda useState och useEffect tillsammans för datahämtning

En av de vanligaste kombinationerna i verkligheten är att använda useState och useEffect att hämta data från ett APIDu behåller data (och kanske loading/error-flaggor) i tillstånd och utför begäran i en effekt som körs när komponenten monteras eller när någon parameter ändras.

Ett grundläggande mönster för att hämta data vid montering ser ut så här:

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>
  );
}

Här säkerställer den tomma beroendematrisen att begäran körs exakt en gångDet interna ignore flagga är ett enkelt sätt att undvika att sätta state på en omonterad komponent ifall begäran löses sent.

Det är också mycket vanligt att lägga till en laddningsflagga och visa en snurrare eller platshållare medan data är på väg:

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>
  );
};

Om din fråga är beroende av en parameter (som en kategori, ett filter eller en ruttparameter), lägg till den parametern i beroendematrisen. så effekten körs om när den ändras:

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

  fetchItems();
}, );

Tänka i "effekter på varje rendering" kontra "livscykler"

Om du är van vid kursmoment kan det vara frestande att mentalt kartlägga useEffect att montera/uppdatera/avmontera metoder, men det leder oftast till mer förvirring. En enklare mental modell är: ”effekter körs efter rendering och kan rensas upp före nästa körning”.

I klasserna var man ofta tvungen att duplicera logik mellan componentDidMount och componentDidUpdate eftersom du ville att samma effekt skulle köras både vid montering och uppdateringar. Med hooks försvinner den dubbleringen: en enda effekt täcker båda fallen, och React tar hand om att rensa upp mellan körningarna.

Denna design eliminerar också en hel klass av buggar kring att inte hantera uppdateringar korrektTill exempel, i en klasskomponent som prenumererar på en väns onlinestatus är det lätt att glömma att prenumerera igen när props.friend ändringar, vilket orsakar inaktuella prenumerationer eller krascher vid avmontering. Med useEffect som listar friend.id Som ett beroende kommer React automatiskt att köra rensningen för den gamla vännen och installationen för den nya.

Tänk på att i utvecklingsläge i strict läge kör React medvetet din installations- och rensningscykel två gånger vid montering.Detta händer inte i produktion, men det är ett användbart stresstest för att bekräfta att din rensning verkligen ångrar allt och att din effekt säkert kan köras flera gånger.

Optimering och felsökning av useEffect-beteende

När en effekt körs oftare än förväntat är det första du bör kontrollera beroendematrisenAntingen ändras ett beroende vid varje rendering (vanligt med inline-objekt/funktioner) eller så glömde du att ange arrayen alls.

Att logga beroendevärdena är ett snabbt sätt att felsöka:

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

Om du ser olika loggar varje gång, kontrollera vilket beroende som faktiskt ändrasOfta återskapas ett inline-objekt eller en pilfunktion varje gång man renderar. Objektskapandet flyttas inuti effekten, funktioner lyfts utanför komponenten eller memoreras med useCallback kan stabilisera beroenden vid behov.

Oändliga loopar uppstår när en effekt både är beroende av ett värde och ovillkorligen uppdaterar samma värde.. Till exempel:

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

Varje gång count ändringar, effekten körs, uppdateringar count igen, utlöser en annan rendering, och så vidareFör att bryta det mönstret, överväg om tillståndsuppdateringen verkligen hör hemma i en effekt, om den istället bör utlösas av en användarinteraktion, eller om du kan förlita dig på ett annat värde.

Ibland vill man läsa det senaste värdet för ett tillstånd eller någon rekvisita inuti en effekt utan att det värdet utlöser en omkörning.I dessa avancerade scenarier, nyare API:er som "effekthändelser" (via useEffectEvent i React-dokumentationen) eller referenser kan hjälpa, men i de flesta praktiska fall är det säkrare och enklare att hålla sig till beroenden.

Att sätta ihop allting, använda useState och useEffect kokar korrekt ner till några grundläggande vanor: håll tillståndet litet och fokuserat, föredra funktionella uppdateringar när nya tillstånd härleds från gamla, strukturera effekter kring setup/cleanup-par, var ärlig och tydlig med beroendematriser och respektera alltid reglerna för hooks så att React på ett tillförlitligt sätt kan spåra vad som hör hemma var. När du följer dessa principer förblir dina komponenter förutsägbara, dina bieffekter beter sig korrekt och din React-kodbas blir mycket lättare att utveckla allt eftersom din app växer.

Relaterad artikel:
Löst: Hur man installerar react native krokar med
Relaterade inlägg: