React Native Academy · leksjon

Håndtere komponenttilstand med useState

Ta i bruk useState-hooken, lagre en teller eller en bryterverdi i tilstanden, og utløs ny gjengivelse ved å kalle funksjonen som setter tilstanden.

Leksjon 2 av 413 trinn

Håndtere komponenttilstand med useState er en gratis leksjon i React Native Academy på CoddyKit. Dette er leksjon 2 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i React Native Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i React Native Academy inneholder totalt 4 leksjoner.

Hva er state?

State er data som kan endres over tid, og som en komponent eier og håndterer internt. I motsetning til props (som kommer fra forelderen og er skrivebeskyttede) opprettes og oppdateres state av selve komponenten. Når state endres, gjengir React automatisk komponenten på nytt for å gjenspeile de nye verdiene i brukergrensesnittet. Vanlige eksempler på state er om en modal er åpen, teksten brukeren har skrevet, fanen som er valgt, eller elementer som er hentet fra et API. State er det som gjør React-komponenter interaktive.

import { View, Text, TouchableOpacity } from 'react-native';
import { useState } from 'react';

export default function Counter() {
  // Declare state: count starts at 0
  const [count, setCount] = useState(0);

  return (
    <View style={{ padding: 24, alignItems: 'center' }}>
      <Text style={{ fontSize: 48, fontWeight: 'bold' }}>{count}</Text>
      <TouchableOpacity
        onPress={() => setCount(count + 1)}
        style={{ marginTop: 16, padding: 12, backgroundColor: '#4f86f7', borderRadius: 8 }}
      >
        <Text style={{ color: '#fff', fontSize: 18 }}>Increment</Text>
      </TouchableOpacity>
    </View>
  );
}

Signaturen til useState-hooken

useState er en React-hook som De kaller på øverste nivå i komponentfunksjonen. Den tar ett argument – startverdien for state – og returnerer en tabell med to elementer: den gjeldende state-verdien og en setterfunksjon. De destrukturerer disse ved hjelp av tabelldestrukturering. Navnekonvensjonen er [value, setValue]. Setterfunksjonen er det De kaller for å oppdatere state – state-variabelen må aldri endres direkte. Hver state-variabel er uavhengig; kall useState flere ganger for flere deler av state.

import { useState } from 'react';

function MyComponent() {
  // Syntax: const [value, setter] = useState(initialValue);
  const [count, setCount] = useState(0);          // number
  const [name, setName] = useState('');            // string
  const [isVisible, setIsVisible] = useState(false); // boolean
  const [items, setItems] = useState([]);          // array
  const [user, setUser] = useState(null);          // object or null

  // Update state by calling the setter:
  // setCount(5);          ← sets to 5
  // setName('Alice');     ← sets to 'Alice'
  // setIsVisible(true);   ← sets to true
}

State-oppdateringer utløser ny gjengivelse

Når De kaller en state-setter, planlegger React en ny gjengivelse av komponenten. Under neste gjengivelse returnerer useState den nye verdien. React håndterer dette effektivt – flere state-oppdateringer som skjer i samme hendelsesbehandler, samles, og det utføres én enkelt ny gjengivelse. Det er også viktig å vite at state-verdien i den gjeldende gjengivelsen er uforanderlig. Å kalle setCount(count + 1) endrer ikke count umiddelbart i den gjeldende funksjonskjøringen – den nye verdien vises i neste gjengivelsessyklus.

import { useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';

export default function Example() {
  const [count, setCount] = useState(0);

  function handlePress() {
    // These do NOT stack: count is still 0 in this render
    setCount(count + 1); // schedules count = 1
    setCount(count + 1); // also schedules count = 1, not 2!

    // To correctly base on previous value, use functional update:
    // setCount(prev => prev + 1); // prev = 0, result = 1
    // setCount(prev => prev + 1); // prev = 1, result = 2
  }

  return (
    <TouchableOpacity onPress={handlePress}>
      <Text>{count}</Text>
    </TouchableOpacity>
  );
}

Funksjonelle oppdateringer med forrige state

Når den nye tilstanden avhenger av den forrige tilstanden, skal De alltid bruke formen for funksjonell oppdatering: setState(prev => prev + 1). React garanterer at prev er den nyeste tilstandsverdien, selv om flere oppdateringer grupperes. Dette er avgjørende når De øker tellere, veksler boolske verdier eller legger til elementer i arrayer i rask rekkefølge (for eksempel ved raske trykk på en knapp). Formen med objektliteraler (setState(state + 1)) kan føre til feil når flere oppdateringer skjer før komponenten gjengis på nytt.

import { useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';

export default function SafeCounter() {
  const [count, setCount] = useState(0);

  function increment() {
    // Safe: uses previous state, handles batching correctly
    setCount(prev => prev + 1);
  }

  function decrement() {
    setCount(prev => Math.max(0, prev - 1)); // min 0
  }

  function reset() {
    setCount(0); // direct value ok when not based on previous
  }

  return (
    <View style={{ alignItems: 'center', gap: 12, padding: 24 }}>
      <Text style={{ fontSize: 48 }}>{count}</Text>
      <TouchableOpacity onPress={increment}><Text>+</Text></TouchableOpacity>
      <TouchableOpacity onPress={decrement}><Text>-</Text></TouchableOpacity>
      <TouchableOpacity onPress={reset}><Text>Reset</Text></TouchableOpacity>
    </View>
  );
}

Veksling av boolsk tilstand

Et svært vanlig mønster er å veksle en boolsk tilstand mellom true og false. Bruk formen for funksjonell oppdatering sammen med den logiske NOT-operatoren: setIsOpen(prev => !prev). Dette er ryddigere enn setIsOpen(!isOpen), fordi det håndterer raske trykk korrekt. Vanlige bruksområder er å vise eller skjule en modal, utvide eller skjule en del av et trekkspill, bytte mellom avspilling og pause eller veksle et favorittstjerneikon. Boolske verdier er kanskje den mest brukte typen komponenttilstand.

import { useState } from 'react';
import { View, Text, TouchableOpacity, Modal, StyleSheet } from 'react-native';

export default function ToggleModal() {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <View style={styles.container}>
      <TouchableOpacity
        style={styles.btn}
        onPress={() => setIsVisible(prev => !prev)}
      >
        <Text style={styles.btnText}>Toggle Modal</Text>
      </TouchableOpacity>

      <Modal visible={isVisible} transparent animationType='fade'>
        <View style={styles.overlay}>
          <Text style={styles.modalText}>Hello from the modal!</Text>
          <TouchableOpacity onPress={() => setIsVisible(false)}>
            <Text style={{ color: '#fff', marginTop: 16 }}>Close</Text>
          </TouchableOpacity>
        </View>
      </Modal>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  btn: { padding: 12, backgroundColor: '#4f86f7', borderRadius: 8 },
  btnText: { color: '#fff', fontWeight: 'bold' },
  overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.7)', justifyContent: 'center', alignItems: 'center' },
  modalText: { color: '#fff', fontSize: 20, fontWeight: 'bold' },
});

Tilstand med strenger: Kontrollert tekstinndata

Ved å koble en TextInput til tilstanden oppretter De en kontrollert inndata. value-propen angir teksten som vises (fra tilstanden), og onChangeText-tilbakekallet oppdaterer tilstanden hver gang brukeren skriver. Dermed blir tilstanden den eneste sannhetskilden for inndataens verdi — De kan når som helst lese, validere eller omforme teksten ved å lese tilstandsvariabelen. Uten å koble inndataen til tilstanden (ukontrollert) måtte De brukt en ref for å lese inndataverdien, noe som er mindre idiomatisk i React.

import { useState } from 'react';
import { View, Text, TextInput, StyleSheet } from 'react-native';

export default function SearchBox() {
  const [query, setQuery] = useState('');

  return (
    <View style={styles.container}>
      <TextInput
        value={query}
        onChangeText={setQuery} // same as: (text) => setQuery(text)
        placeholder='Search...'
        style={styles.input}
      />
      <Text style={styles.preview}>
        You typed: <Text style={{ fontWeight: 'bold' }}>{query}</Text>
      </Text>
      <Text style={styles.count}>{query.length} characters</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { padding: 16, gap: 8 },
  input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 12, fontSize: 16 },
  preview: { fontSize: 14, color: '#555' },
  count: { fontSize: 12, color: '#999' },
});

Tilstand med arrayer: Legge til og fjerne elementer

Når tilstanden er en array, må De aldri endre den direkte med push eller splice. Opprett i stedet en ny array med endringen, og send den til setter-funksjonen. Slik legger De til et element: setItems(prev => [...prev, newItem]). Slik fjerner De et element: setItems(prev => prev.filter(item => item.id !== id)). Slik oppdaterer De et element: setItems(prev => prev.map(item => item.id === id ? {...item, done: true} : item)). Disse uforanderlige mønstrene gjør at React kan oppdage endringer og planlegge effektive gjengivelser på nytt.

import { useState } from 'react';
import { View, Text, TouchableOpacity, TextInput, FlatList } from 'react-native';

export default function TodoList() {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState('');

  function addTodo() {
    if (!input.trim()) return;
    setTodos(prev => [...prev, { id: Date.now().toString(), text: input.trim(), done: false }]);
    setInput('');
  }

  function toggleDone(id) {
    setTodos(prev => prev.map(t => t.id === id ? { ...t, done: !t.done } : t));
  }

  return (
    <View style={{ flex: 1, padding: 16 }}>
      <TextInput value={input} onChangeText={setInput} placeholder='Add a todo' />
      <TouchableOpacity onPress={addTodo}><Text>Add</Text></TouchableOpacity>
      <FlatList
        data={todos}
        keyExtractor={t => t.id}
        renderItem={({ item }) => (
          <TouchableOpacity onPress={() => toggleDone(item.id)}>
            <Text style={{ textDecorationLine: item.done ? 'line-through' : 'none' }}>
              {item.text}
            </Text>
          </TouchableOpacity>
        )}
      />
    </View>
  );
}

Tilstand med objekter

Gruppér relaterte tilstandsverdier i én enkelt tilstandsvariabel som er et objekt. Når De oppdaterer objekttilstanden, skal De spre den forrige tilstanden for å beholde felter som ikke endres: setState(prev => ({ ...prev, name: 'Alice' })). Dette overskriver bare feltet name, mens alle andre felter beholdes uendret. Alternativt kan hvert felt ha sin egen useState — ingen av tilnærmingene er alltid bedre enn den andre. Bruk et objekt når feltene alltid endres sammen (for eksempel i et skjema med navn, e-postadresse og passord), og separate tilstander når de endres uavhengig av hverandre.

import { useState } from 'react';
import { View, TextInput, Text, StyleSheet } from 'react-native';

export default function ProfileForm() {
  const [form, setForm] = useState({
    name: '',
    email: '',
    bio: '',
  });

  function updateField(field, value) {
    setForm(prev => ({ ...prev, [field]: value }));
  }

  return (
    <View style={{ padding: 16, gap: 12 }}>
      <TextInput
        value={form.name}
        onChangeText={v => updateField('name', v)}
        placeholder='Name'
        style={styles.input}
      />
      <TextInput
        value={form.email}
        onChangeText={v => updateField('email', v)}
        placeholder='Email'
        style={styles.input}
      />
      <Text style={{ color: '#666' }}>Preview: {form.name} — {form.email}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 12 },
});

Lat initialtilstand

Hvis det er kostbart å beregne initialtilstanden (for eksempel å lese fra AsyncStorage eller analysere et stort datasett), skal De sende en funksjon til useState i stedet for en verdi: useState(() => computeExpensiveValue()). React kaller denne initialiseringsfunksjonen bare én gang, ved komponentens første gjengivelse, og ikke ved senere gjengivelser. Dette kalles lat initialisering. Hvis De sender en kostbar beregning som en vanlig verdi (useState(expensiveCompute())), kjøres den ved hver gjengivelse. Det fører til unødvendig arbeid, selv om initialtilstanden bare brukes én gang.

import { useState } from 'react';

// Without lazy init: computeExpensiveList() runs on EVERY render
const [list, setList] = useState(computeExpensiveList()); // BAD

// With lazy init: function is called ONCE on mount
const [list, setList] = useState(() => computeExpensiveList()); // GOOD

// Real example: JSON.parse is cheap, but still good practice:
const [settings, setSettings] = useState(() => {
  const stored = SomeStorage.get('settings');
  return stored ? JSON.parse(stored) : { theme: 'light', lang: 'en' };
});

Avledede verdier kontra overflødig tilstand

Ikke lagre verdier i tilstanden hvis de kan avledes fra eksisterende tilstand eller pro­ps. Hvis De for eksempel har en liste over gjøremål i tilstanden, skal De ikke også lagre en completedCount i tilstanden — beregn den som todos.filter(t => t.done).length under gjengivelsen. Lagring av avledede verdier i tilstanden skaper synkroniseringsfeil: De kan glemme å oppdatere den ene når De oppdaterer den andre. Legg bare ting i tilstanden som De ikke kan avlede, ikke kan beregne under gjengivelsen, eller som settes asynkront (for eksempel data fra et API-svar). Denne regelen holder tilstanden minimal og fri for feil.

import { useState } from 'react';
import { Text, View } from 'react-native';

export default function TodoStats() {
  const [todos, setTodos] = useState([
    { id: '1', text: 'Buy groceries', done: true },
    { id: '2', text: 'Write code', done: false },
    { id: '3', text: 'Go for a walk', done: true },
  ]);

  // DERIVED: computed from existing state, not stored in state
  const total = todos.length;
  const completed = todos.filter(t => t.done).length;
  const remaining = total - completed;

  return (
    <View style={{ padding: 16 }}>
      <Text>Total: {total}</Text>
      <Text>Completed: {completed}</Text>
      <Text>Remaining: {remaining}</Text>
    </View>
  );
}

useState kontra useRef: Når tilstand ikke bør brukes

Ikke alle verdier som endres i en komponent, trenger å være i tilstanden. Hvis en verdi endres, men ikke trenger å utløse en ny gjengivelse, skal De lagre den i en useRef i stedet. Vanlige eksempler er en tidtaker-ID returnert fra setTimeout, et flagg som angir om en nettverksforespørsel pågår, eller den forrige verdien til en prop. Hvis De legger disse i tilstanden, fører det til unødvendige gjengivelser. En useRef beholder verdien på tvers av gjengivelser, akkurat som tilstand, men oppdatering av den planlegger ikke en ny gjengivelse. Tommelfingerregelen er: Hvis brukergrensesnittet ikke trenger å endres når verdien endres, skal De bruke useRef.

import { useState, useRef, useEffect } from 'react';
import { Text, TouchableOpacity, View } from 'react-native';

export default function Timer() {
  const [elapsed, setElapsed] = useState(0);
  const intervalRef = useRef(null); // timer ID in ref — no re-render needed

  function start() {
    if (intervalRef.current) return; // already running
    intervalRef.current = setInterval(() => {
      setElapsed(prev => prev + 1); // state — needs re-render to show
    }, 1000);
  }

  function stop() {
    clearInterval(intervalRef.current);
    intervalRef.current = null;
  }

  useEffect(() => () => clearInterval(intervalRef.current), []); // cleanup

  return (
    <View style={{ alignItems: 'center', padding: 24, gap: 16 }}>
      <Text style={{ fontSize: 48 }}>{elapsed}s</Text>
      <TouchableOpacity onPress={start}><Text>Start</Text></TouchableOpacity>
      <TouchableOpacity onPress={stop}><Text>Stop</Text></TouchableOpacity>
    </View>
  );
}

Kort kontroll

Test forståelsen Deres av konseptene innen React Native Mobile Development fra denne leksjonen.

Oppsummering av leksjonen

I denne leksjonen har De lært at useState erklærer tilstandsvariabler og tilbyr en setter som utløser nye gjengivelser, at funksjonelle oppdateringer på en trygg måte utleder ny tilstand fra den forrige verdien, og at avledede verdier bør beregnes under gjengivelsen i stedet for å lagres som overflødig tilstand. Deretter skal vi se på hvordan tilstanden kan løftes opp for å dele data mellom søskenkomponenter.

Gratis å komme i gang

Lær deg JavaScript med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
30
Leksjoner
120

Ofte stilte spørsmål

Er leksjonen «Håndtere komponenttilstand med useState» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien React Native Academy, inkludert «Håndtere komponenttilstand med useState», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i React Native Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Håndtere komponenttilstand med useState»?

Ta i bruk useState-hooken, lagre en teller eller en bryterverdi i tilstanden, og utløs ny gjengivelse ved å kalle funksjonen som setter tilstanden. Du øver på React Native Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med React Native Academy?

Ingen tidligere erfaring er nødvendig. React Native Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.

Hvor lang tid tar leksjonen «Håndtere komponenttilstand med useState»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne React Native Academy-leksjonen?

Ja. Alle React Native Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Sende data med props
  2. Håndtere komponenttilstand med useState
  3. Løfte tilstanden opp
  4. Bygge en interaktiv tellerapp
← Tilbake til React Native Academy