React Native Academy · Lektion

useReducer för komplex tillståndslogik

Ersätt flera useState-anrop med en enda useReducer, skriv en reducerfunktion med åtgärdstyper och skicka åtgärder från UI-händelser.

Lektion 2 av 413 steg

useReducer för komplex tillståndslogik är en gratis lektion i React Native Academy på CoddyKit. Detta är lektion 2 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för React Native Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i React Native Academy innehåller totalt 4 lektioner.

Varför useReducer?

När en komponent har flera useState-anrop vars uppdateringar hänger ihop kan logiken bli svår att följa. useReducer samlar alla tillståndsövergångar i en enda reducer-funktion, vilket gör komplex tillståndslogik enklare att förstå, testa och felsöka – samma mönster som populariserades av Redux.

Reducer-funktionen

En reducer är en ren funktion som tar emot det aktuella tillståndet och ett action-objekt och returnerar nästa tillstånd. Den måste vara ren – inga sidoeffekter, inga API-anrop och inga ändringar av det inkommande tillståndsobjektet. Returnera ett nytt objekt för det nya tillståndet. Strängen action.type identifierar vilken övergång som ska utföras.

function counterReducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return { count: 0 };
    default:
      return state;
  }
}

Anropa useReducer

useReducer tar emot reducer-funktionen och ett initialt tillstånd och returnerar [state, dispatch]. state är det aktuella tillståndsvärdet. Funktionen dispatch skickar en action till reducern för att utlösa en tillståndsövergång och schemalägga en omrendering.

import { useReducer } from 'react';

const initialState = { count: 0 };

function Counter() {
  const [state, dispatch] = useReducer(counterReducer, initialState);

  return (
    <View>
      <Text>Count: {state.count}</Text>
      <Button title='+' onPress={() => dispatch({ type: 'increment' })} />
      <Button title='-' onPress={() => dispatch({ type: 'decrement' })} />
      <Button title='Reset' onPress={() => dispatch({ type: 'reset' })} />
    </View>
  );
}

Skicka payload med actions

Actions kan innehålla ytterligare data i en egenskap med namnet payload. Reducern läser action.payload för att känna till detaljerna i övergången. En action av typen 'addItem' kan till exempel innehålla det nya objektets data i sin payload, som reducern lägger till i tillståndets array med objekt.

function listReducer(state, action) {
  switch (action.type) {
    case 'addItem':
      return { items: [...state.items, action.payload] };
    case 'removeItem':
      return { items: state.items.filter((i) => i.id !== action.payload.id) };
    default:
      return state;
  }
}

// Dispatch with payload
dispatch({ type: 'addItem', payload: { id: '1', text: 'Buy milk' } });

Hantera en varukorg

En varukorg är ett utmärkt verkligt användningsfall för useReducer eftersom den omfattar flera relaterade tillståndsövergångar: lägga till objekt, ta bort objekt, uppdatera antal och tömma varukorgen. Varje action-typ motsvarar en tydlig och testbar övergång i reducer-funktionen.

const initialCartState = { items: [], total: 0 };

function cartReducer(state, action) {
  switch (action.type) {
    case 'ADD':
      return {
        items: [...state.items, action.payload],
        total: state.total + action.payload.price,
      };
    case 'CLEAR':
      return initialCartState;
    default:
      return state;
  }
}

Initiera tillstånd lazy

Skicka ett tredje argument till useReducer – en init-funktion – för att beräkna det initiala tillståndet lazy. React anropar init(initialArg) i stället för att använda initialArg direkt. Detta är användbart när det initiala tillståndet härleds från en prop eller kräver en kostsam beräkning.

function init(initialCount) {
  return { count: initialCount };
}

function Counter({ startCount }) {
  const [state, dispatch] = useReducer(
    counterReducer,
    startCount, // passed to init
    init         // called once on mount
  );
}

useReducer jämfört med useState

Använd useState för enkla, oberoende värden, till exempel en boolean, en sträng eller ett tal. Använd useReducer när tillståndsövergångar beror på flera värden samtidigt, när nästa tillstånd beror på det aktuella tillståndet på komplexa sätt eller när Ni vill samla all tillståndslogik i en testbar funktion.

// useState — fine for simple independent values
const [name, setName] = useState('');
const [age, setAge] = useState(0);

// useReducer — better when these fields relate to each other
const [userForm, dispatch] = useReducer(userFormReducer, {
  name: '',
  age: 0,
  isValid: false,
});

Formulärtillstånd med useReducer

Ett komplext registreringsformulär med många fält och valideringslogik har nytta av useReducer. Definiera action-typer som 'CHANGE_FIELD', 'VALIDATE' och 'SUBMIT'. Reducern hanterar alla fältuppdateringar och all validering på ett ställe i stället för att logiken sprids ut över flera useState-setters och useEffect-anrop.

function formReducer(state, action) {
  switch (action.type) {
    case 'CHANGE_FIELD':
      return {
        ...state,
        values: { ...state.values, [action.field]: action.value },
        errors: { ...state.errors, [action.field]: null },
      };
    case 'SET_ERROR':
      return { ...state, errors: { ...state.errors, ...action.errors } };
    default:
      return state;
  }
}

Testa en reducer isolerat

Eftersom reducers är rena funktioner utan sidoeffekter är de mycket enkla att enhetstesta. Anropa reducern direkt med ett tillstånd och en action och kontrollera det returnerade tillståndet. Ni behöver inte montera någon komponent eller fejka något API för att testa logiken.

// Pure unit test — no React needed
test('increment action increases count by 1', () => {
  const state = { count: 5 };
  const next = counterReducer(state, { type: 'increment' });
  expect(next.count).toBe(6);
});

test('reset returns count to 0', () => {
  const state = { count: 100 };
  const next = counterReducer(state, { type: 'reset' });
  expect(next.count).toBe(0);
});

Kombinera useReducer med useContext

Ett kraftfullt mönster är att kombinera useReducer med useContext för att bygga en liten global store som liknar Redux. Exportera funktionen dispatch via context så att alla underordnade komponenter kan skicka actions utan prop drilling. På så sätt slipper Ni installera ett tredjepartsbibliotek i appar med måttlig komplexitet.

const StoreContext = createContext(null);
const DispatchContext = createContext(null);

export function StoreProvider({ children }) {
  const [state, dispatch] = useReducer(appReducer, initialState);
  return (
    <StoreContext.Provider value={state}>
      <DispatchContext.Provider value={dispatch}>
        {children}
      </DispatchContext.Provider>
    </StoreContext.Provider>
  );
}

Skicka actions från flera komponenter

När dispatch-funktionen är tillgänglig via context kan alla nästlade komponenter utlösa tillståndsövergångar utan att ta emot props. Importera hooken useContext(DispatchContext), anropa dispatch med rätt action-typ och låt reducern hantera uppdateringen centralt. Detta skalar bra när appen växer.

function AddToCartButton({ product }) {
  const dispatch = useContext(DispatchContext);

  return (
    <Button
      title='Add to Cart'
      onPress={() => dispatch({ type: 'ADD', payload: product })}
    />
  );
}

Snabbkontroll

Testa Er förståelse av useReducer för komplex tillståndslogik från den här lektionen.

Sammanfattning av lektionen

I den här lektionen har Ni lärt Er att: useReducer samlar tillståndsövergångar i en ren reducer-funktion, actions skickas med en typ och en valfri payload för att utlösa övergångar och useReducer kan kombineras med useContext för ett skalbart mönster för globalt tillstånd utan Redux. Härnäst utforskar vi useCallback och useMemo för prestandaoptimering.

Gratis att börja

Lär dig JavaScript med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
30
Lektioner
120

Vanliga frågor

Är lektionen ”useReducer för komplex tillståndslogik” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen React Native Academy, inklusive ”useReducer för komplex tillståndslogik”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i React Native Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”useReducer för komplex tillståndslogik”?

Ersätt flera useState-anrop med en enda useReducer, skriv en reducerfunktion med åtgärdstyper och skicka åtgärder från UI-händelser. Ni övar på React Native Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig React Native Academy?

Du behöver inga förkunskaper. Utbildningen i React Native Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”useReducer för komplex tillståndslogik”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här React Native Academy-lektionen?

Ja. Varje React Native Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. useRef för DOM-noder och föränderliga värden
  2. useReducer för komplex tillståndslogik
  3. useCallback och useMemo för bättre prestanda
  4. Skriva anpassade hooks
← Tillbaka till React Native Academy