Go to blue arrow
back to Tech Blog
Udvikling
Tiago Madeira
Alexandra Mendes

12. august 2026

Min Read

React Native og Redux: Sådan kommer du i gang

Åben bærbar med JavaScript-kode på hvidt skrivebord ved plante og krus, til React Native Redux.

React Native og Redux er den kombination, de fleste teams vælger, når en app vokser ud af lokal komponenttilstand. Denne guide viser, hvordan du forbinder de to, hvordan du bevarer tilstanden på tværs af genstarter, og hvordan du vurderer, om mønsteret overhovedet er værd at tage i brug. Tænk på det sådan her: Uden et lager bliver tilstanden sendt ned gennem komponenttræet som en pakke, der sendes gennem en korridor, hvor hver komponent kun holder på den for at give den videre. Redux erstatter korridoren med et lagerrum. Ét rum, én logbog, og enhver skærm kan læse, hvad den har brug for, uden at forstyrre naboerne.

Der er to måder at læse denne side på. Hvis du er i gang med at bygge løsningen, så følg gennemgangen fra top til bund. Hvis du er teknisk ansvarlig eller founder og skal beslutte, om I overhovedet skal bruge Redux, så spring direkte til testen med fire spørgsmål om tilstand og FAQ'en. Uanset hvad er det underliggende spørgsmål lige så meget forretningsmæssigt som teknisk: Et forudsigeligt tilstandslag bytter en smule indledende boilerplate ud med hurtigere onboarding og lavere risiko ved ændringer senere, og testen med de fire spørgsmål er måden, du finder ud af, om den investering kan betale sig.

Dette er en begynderguide til Redux og Redux Toolkit i en React Native-applikation, og den forudsætter, at du kender det grundlæggende i React Native. Er du også ny til det? Start med vores guide til React Native og Expoeller den officielle dokumentation til React Native, og vend derefter tilbage.

Vi bruger en Android-emulator gennem hele guiden. Den simulerer en Android-enhed på din computer, så du kan teste på tværs af enheder og Android API-niveauer uden at skulle eje hver enkelt telefon fysisk. Den giver dig næsten alt det, en rigtig enhed gør. Vores guide til React Native og Expo dækker opsætningen af det miljø.

For at gøre tingene konkrete bygger vi en simpel tæller-applikation undervejs. Den kommer til at se nogenlunde således ud:

Tæller-applikation.

Her er koden til vores SimpleCounter -komponent:

// components/SimpleCounter.js
import React from 'react';
import { View, Text, Button, TextInput } from 'react-native';

const SimpleCounter = () => (
  <View>
    <Text>0</Text>
    <Button title="-" onPress={() => {}} />
    <Button title="+" onPress={() => {}} />
    <TextInput keyboardType="numeric" placeholder="Amount" />
    <Button title="Change by amount" onPress={() => {}} />
  </View>
);

export default SimpleCounter;

Lige nu er koden statisk. Der er slet ikke erklæret nogen tilstand inde i komponenten, og alle eksempler, der følger, bygger videre på denne. Når dit miljø er klar, skal du installere redux og react-redux biblioteker:

npm install redux react-redux
En bemærkning om opsætningen: vi installerer den selvstændige redux pakke her, så du kan se mekanikken fra bunden. Til et helt nyt projekt er den pakke, du rent faktisk bør vælge, Redux Toolkit, npm install @reduxjs/toolkit react-redux, som er Redux-teamets anbefalede standard. Vi bygger op til den senere i denne guide, så du forstår, hvad den gør for dig.
blue arrow to the left
Imaginary Cloud logo

Hvad er Redux, og hvorfor bruge det?

Redux er et JavaScript-bibliotek til håndtering af din applikations tilstand. Det giver dig et centraliseret sted kaldet store, hvor tilstanden gemmes og ændres via actions og reducers. Med andre ord: et lagerlokale kombineret med en logbog, der holder styr på, hvem der har ændret hvad.

Dette centrale sted gør det muligt at dele tilstand mellem skærme og vide præcis, hvor og hvordan den bliver ændret. Det er uundværligt i voksende applikationer – altså dem, som ingen kan overskue i deres helhed, og som netop derfor bliver fyldt med fejl.

blue arrow to the left
Imaginary Cloud logo

Sådan fungerer Redux: actions, reducers og store

Redux styrer applikationstilstanden gennem en kombination af actions, reducers og store. Lær hvordan disse tre abstraktioner hænger sammen, så er resten blot detaljer.

Redux dataflow-diagram, der viser handlinger, reducere og store-opdateringer i en React Native tæller-app.

Hvad applikationstilstand betyder i Redux

Applikationstilstand er al den information, din app bruger eller ændrer. Centralisér den, giv den en forudsigelig måde at ændre sig på, så holder dine skærmbilleder op med at være uenige om, hvad der er sandt.

Dette er vigtigst i store single-page-applikationer, altså apps der indlæses én gang og derefter opdaterer skærmen løbende i stedet for at hente en ny side for hver interaktion.

Actions og reducers: hvad er ændret, og hvordan

Actions og reducers ændrer tilstanden i fællesskab. Actions bestemmer, hvad der bliver ændret, og hvor. Reducers specificerer hvordan. Hvis vi ser på tæller-layoutet fra starten af dette indlæg, skal vi bruge tre actions: INCREMENT, DECREMENT, og CHANGE_BY_AMOUNT.

Actions er objekter med en type og en payload attribut. Typen er actionens identifikator, og payloaden indeholder alt det, som reduceren skal bruge for at ændre tilstanden. Vores første to actions flytter kun tælleren med 1, så de behøver kun en type og intet andet. Den tredje kræver en payload for at angive, hvor meget der skal ændres.

Deklarér dine actions i en separat fil kaldet Actions:

// redux/Actions.js
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const CHANGE_BY_AMOUNT = 'CHANGE_BY_AMOUNT';

export const increment = () => ({ type: INCREMENT });
export const decrement = () => ({ type: DECREMENT });
export const changeByAmount = (amount) => ({
  type: CHANGE_BY_AMOUNT,
  payload: { amount },
});

Navngiv handlinger efter hvad der skete, ikke efter den handler, du vil køre. I vores projekter overlever handlingsnavne, der læses som begivenheder, refaktoreringer. Navne, der læses som funktionskald, bliver omdøbt, første gang to skærme har brug for den samme ændring. Redux style guide argumenterer for det samme, hvis du vil have den fulde begrundelse.

Dernæst den initiale tilstand. Den ligger i en anden fil ved siden af reduceren:

// redux/Reducer.js
import { INCREMENT, DECREMENT, CHANGE_BY_AMOUNT } from './Actions';

const initialState = {
  counter: { amount: 0 },
};

Den initiale tilstand indeholder et objekt ved navn counter med en amount attribut, der starter på 0. Du vil bemærke, at det er en konstant frem for en variabel, hvilket virker mærkeligt for noget, der beskrives som tilstand. Vi vender tilbage til hvorfor.

Nu til reduceren, en funktion der tager den nuværende tilstand og handlingen som argumenter og producerer den nye tilstand:

// redux/Reducer.js (continued)
const counterReducer = (state = initialState, action) => {
  switch (action.type) {
    case INCREMENT:
      return { ...state, counter: { amount: state.counter.amount + 1 } };
    case DECREMENT:
      return { ...state, counter: { amount: state.counter.amount - 1 } };
    case CHANGE_BY_AMOUNT:
      return {
        ...state,
        counter: { amount: state.counter.amount + action.payload.amount },
      };
    default:
      return state;
  }
};

export default counterReducer;

Vi må aldrig mutere tilstanden inde i en reducer. Hvorfor ikke? Fordi reduceren ikke bør ændre tilstandsobjektet direkte; den bør returnere et nyt objekt, som bliver den nye tilstand. Reacts rendering-motor sammenligner det forrige tilstandsobjekt med det nyeste for at beslutte, hvad der skal tegnes igen.

Hvis du ændrer tilstanden direkte, ser React ingen ændring, og den har derfor en fejlagtig opfattelse af, hvordan din app ser ud lige nu. Redux-dokumentationen om immutable update patterns forklarer mekanikken i detaljer.

Det er derfor, vi erklærede den initiale tilstand som en konstant tidligere. Det er det ene objekt på lageret, som ingen må skrive på.

Oprettelse af Redux-storen

Næste skridt er selve storen, objektet hvor tilstanden gemmes. Det er almindelig praksis at oprette den og eksportere den fra sin egen fil:

// redux/Store.js
import { createStore } from 'redux';
import counterReducer from './Reducer';

const store = createStore(counterReducer);

export default store;
Bemærk: createStore er officielt udgået fra og med Redux 5.0.0, så den vil fremstå med gennemstregning i din editor. Den virker stadig og vil fortsat virke, så den er fin til at lære mekanismerne her, men Redux-teamet fraråder at bruge den, eller den selvstændige redux pakke, direkte i ny kode. Den moderne erstatning er Redux Toolkits configureStore, som vi går videre til senere i denne guide. Hvis du ser en advarsel om forældelse på dette trin, er det forventet.

Vi opretter storen med Redux' createStore() metode, hvor vi sender den reducer-funktion med, som vi definerede tidligere. Med storen på plads kan vi aktivere handlinger via dispatch-metoden, som vises senere i denne guide, for at ændre tilstanden.

Nu gør vi storen tilgængelig ved at sende den til Provider komponenten, der omslutter SimpleCounter. Provider giver storen videre til den komponent og alt, hvad der er indeni den.

Provider kommer fra react-redux, det officielle bindingsbibliotek til React og React Native med Redux. Sådan ser det ud:

// App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './redux/Store';
import SimpleCounter from './components/SimpleCounter';

const App = () => (
  <Provider store={store}>
    <SimpleCounter />
  </Provider>
);

export default App;

Forbind dine React-komponenter til Redux-lageret

Nu forbinder vi vores komponenter til lageret. I moderne React Native-kodebaser gør du det med to hooks fra react-redux: useSelector læser en del af tilstanden, og useDispatch returnerer dispatch-metoden, så komponenten kan sende handlinger tilbage.

// components/SimpleCounter.js
import React, { useState } from 'react';
import { View, Text, Button, TextInput } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, changeByAmount } from '../redux/Actions';

const SimpleCounter = () => {
  const amount = useSelector((state) => state.counter.amount);
  const dispatch = useDispatch();
  const [inputValue, setInputValue] = useState('0');

  return (
    <View>
      <Text>{amount}</Text>
      <Button title="-" onPress={() => dispatch(decrement())} />
      <Button title="+" onPress={() => dispatch(increment())} />
      <TextInput
        keyboardType="numeric"
        value={inputValue}
        onChangeText={setInputValue}
      />
      <Button
        title="Change by amount"
        onPress={() => dispatch(changeByAmount(Number(inputValue)))}
      />
    </View>
  );
};

export default SimpleCounter;

Vi har kun adgang til tilstandsobjektet inde i useSelector fordi SimpleCounter er pakket ind i Provider. Selectoren modtager hele tilstanden og returnerer kun den værdi, komponenten har brug for, så komponenten genindlæses, når state.counter.amount ændrer sig, og forbliver uændret, når en urelateret del af lageret opdateres.

Hold dine selectors snævre af den grund. Den mest almindelige præstationsklage, vi hører om Redux i React Native, er en liste, der genindlæses ved hvert tastetryk, og det er næsten altid en selector, der returnerer en hel del eller et nyligt oprettet objekt, hvor en enkelt værdi ville have været tilstrækkelig.

Ældre kodebaser forbinder komponenter med connect -metoden og en mapStateToProps -funktion i stedet. Den fletter objektet, der returneres fra mapStateToProps , ind i komponentens props, så den samme værdi ankommer som this.props.amount:

// legacy pattern, still valid in existing codebases
const mapStateToProps = (state) => ({ amount: state.counter.amount });

export default connect(mapStateToProps)(SimpleCounter);

Begge tilgange kommunikerer med den samme store. Hooks er det anbefalede mønster til ny kode, og det vi bruger i resten af denne guide. Lær connect alligevel, da du vil støde på det i ethvert React Native-projekt, der er skrevet, før hooks blev introduceret.

blue arrow to the left
Imaginary Cloud logo

Tilstandspersistens i React Native

Vi har altså et centraliseret lager, der gemmer og ændrer tilstand på forudsigelig vis via actions og reducers. Du har måske bemærket en mangel. Luk appen, åbn den igen, og tælleren er tilbage på nul.

Det skyldes, at intet gemmer tilstanden permanent. Hver gang applikationen starter, nulstiller reduceren tælleren til dens oprindelige værdi.

Persistens er vigtig, når information skal overleve sessionen: login-tokens, konfigurationsindstillinger eller et udkast, som brugeren var i gang med. I React Native får du det med redux-persist -biblioteket.

Redux Persist skriver lageret til lokal persistent lagring og læser det tilbage, hver gang appen genåbnes eller opdateres. Vi bruger en Android-emulator her, men det fungerer lige så fint på iOS. Start med at installere det sammen med AsyncStorage, det key-value-lager, som det skriver til:

npm install redux-persist @react-native-async-storage/async-storage

Derefter skal du ændre Store.js -filen således:

// redux/Store.js
import { createStore } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';
import counterReducer from './Reducer';

const persistConfig = {
  key: 'root',
  storage: AsyncStorage,
};

const persistedReducer = persistReducer(persistConfig, counterReducer);

export const store = createStore(persistedReducer);
export const persistor = persistStore(store);

Importér persistStore og persistReducer fra redux-persist. Send din reducer til persistReducer sammen med persistConfig objektet, og du får en persistedReducer tilbage.

I persistConfig erklærer du, at AsyncStorage skal indeholde lageret. AsyncStorage er React Natives key-value-lager, og det er ukrypteret, så alt følsomt indhold, og især godkendelsestokens, bør i stedet placeres i sikker lagring.

Web- og mobiludviklingsbanner: isometrisk skærm og smartphone-app med React-logo.

Til sidst skal du kalde persistStore for at holde lageret persistent. I et større projekt ønsker du måske ikke, at hele tilstanden skrives til disken, og det er det, whitelist- og blacklist-indstillingerne i persistConfig er til for: vælg de reducere, der er værd at gemme, og udelad resten. redux-persist README dokumenterer begge dele.

Til sidst, i App.js, skal du importere persistor fra Store.js og omslut SimpleCounter med PersistGate. Den holder brugerfladen tilbage, indtil den gemte tilstand er hentet og indlæst i storen, så brugerne aldrig ser den oprindelige tilstand blinke, før den rigtige er på plads:

// App.js
import React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from './redux/Store';
import SimpleCounter from './components/SimpleCounter';

const App = () => (
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <SimpleCounter />
    </PersistGate>
  </Provider>
);

export default App;
blue arrow to the left
Imaginary Cloud logo

Redux Toolkit: mindre boilerplate for den samme funktionalitet

Redux Toolkit er et bibliotek skrevet af Redux-udviklerne for at hjælpe dig med at skabe mere effektiv Redux-logik. Det er det, Redux-teamet anbefaler til nye projekter, og det erstatter det meste af koden ovenfor med en brøkdel af den.

Det er også her, økosystemet er landet: Redux Toolkit er Redux-teamets officielle anbefaling til alle nye projekter, og størstedelen af React-Redux-installationer inkluderer det nu frem for at bygge Redux fra bunden. Vi bruger det også i produktion. TrustPortal kører på Redux Toolkit for at holde tilstanden forudsigelig på tværs af en stor virksomhedsapplikation, hvilket er præcis den gevinst, der beskrives her. Nedenfor er en gennemgang af de funktioner, der betyder mest.

createAction() til deklarering af actions

Redux Toolkit giver os en ny måde at oprette en action på:

import { createAction } from '@reduxjs/toolkit';

export const increment = createAction('INCREMENT');
export const decrement = createAction('DECREMENT');
export const changeByAmount = createAction('CHANGE_BY_AMOUNT');

createAction tager action-typen som et argument og returnerer en action creator-funktion. Kald den funktion, send payloadet med, og du får action-objektet. Du kan også læse typen tilbage med toString() metoden, som i increment.toString(). Slut med at deklarere action-typer som konstanter, hvilket reducerer boilerplate betydeligt.

createReducer() til skrivning af reducere

createReducer forenkler den anden halvdel. I stedet for en switch-sætning mapper du hver action til den funktion, der håndterer den, hvilket læses langt mere rent, og de action creators, der returneres af createAction() kan sendes direkte til addCase(). Ved at følge den samme reducer-logik som før:

import { createReducer } from '@reduxjs/toolkit';
import { increment, decrement, changeByAmount } from './Actions';

const initialState = { counter: { amount: 0 } };

const counterReducer = createReducer(initialState, (builder) => {
  builder
    .addCase(increment, (state) => ({
      ...state,
      counter: { amount: state.counter.amount + 1 },
    }))
    .addCase(decrement, (state) => ({
      ...state,
      counter: { amount: state.counter.amount - 1 },
    }))
    .addCase(changeByAmount, (state, action) => ({
      ...state,
      counter: { amount: state.counter.amount + action.payload },
    }));
});

createReducer() bruger også Immer, et bibliotek, der lader dig skrive kode, som ser ud til at mutere tilstanden, mens det i baggrunden opretter en ny kopi. Immer oversætter enhver muterende handling til den tilsvarende kopieringshandling. Det betyder, at vi kan skrive vores reducer således:

const counterReducer = createReducer(initialState, (builder) => {
  builder
    .addCase(increment, (state) => {
      state.counter.amount += 1;
    })
    .addCase(decrement, (state) => {
      state.counter.amount -= 1;
    })
    .addCase(changeByAmount, (state, action) => {
      state.counter.amount += action.payload;
    });
});

Betydeligt kortere. Samme funktionalitet.

createSlice() til både actions og reducere

I praksis kalder man sjældent createAction og createReducer hver for sig. createSlice genererer begge dele ud fra én definition: Giv dit slice et navn, en starttilstand og et sæt reducer-funktioner, så returnerer den både reduceren og en tilhørende action creator for hver funktion.

// redux/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { amount: 0 },
  reducers: {
    increment: (state) => {
      state.amount += 1;
    },
    decrement: (state) => {
      state.amount -= 1;
    },
    changeByAmount: (state, action) => {
      state.amount += action.payload;
    },
  },
});

export const { increment, decrement, changeByAmount } = counterSlice.actions;
export default counterSlice.reducer;

Hele Actions.js og Reducer.js parret fra tidligere samles i den ene fil. Dine komponenter fortsætter med at bruge useSelector og useDispatch præcis som før.

configureStore() til oprettelse af storen

configureStore() omslutter createStore() og opsætter fornuftige standardindstillinger undervejs, herunder forbindelsen til Redux DevTools og standard-middleware, hvilket er de funktioner, som enhver handling passerer igennem på vej til reduceren. Da det er den moderne efterfølger til den nu forældede createStore, er det den, du bør vælge til ethvert nyt projekt.

Den tager også imod et konfigurationsobjekt frem for en gruppe af funktioner, så reduceren placeres inde i et objekt under reducer attributten:

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';

export const store = configureStore({
  reducer: { counter: counterReducer },
});

Løsning på 'A non-serializable value was detected in an action'

Brug redux-persist og Redux Toolkit i det samme projekt, og før eller siden vil du støde på fejlen 'A non-serializable value was detected in an action'. Årsagen: configureStore() kører et serialiserbarhedstjek, en beskyttelse under udvikling, der advarer, når en handling indeholder noget, som Redux ikke sikkert kan gemme, såsom en funktion eller et Promise, og redux-persist er nødt til at sende funktioner i sine egne handlinger.

Løsningen er at beholde tjekket aktiveret og undtage redux-persists handlingstyper fra det:

import { configureStore } from '@reduxjs/toolkit';
import {
  persistStore,
  persistReducer,
  FLUSH,
  REHYDRATE,
  PAUSE,
  PERSIST,
  PURGE,
  REGISTER,
} from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';
import counterReducer from './counterSlice';

const persistConfig = { key: 'root', storage: AsyncStorage };
const persistedReducer = persistReducer(persistConfig, counterReducer);

export const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }),
});

export const persistor = persistStore(store);

Bemærk forskellen på at undtage de seks handlingstyper og at slå tjekket helt fra. Slår du det helt fra, mister du advarslen for alle de værdier, der rent faktisk ikke kan serialiseres, som din egen kode placerer i storen. Både diskussionen og løsningen findes i redux-persist issue om serialiserbarhedstjekket.

blue arrow to the left
Imaginary Cloud logo

Async-tilstand: hentning af data med createAsyncThunk og RTK Query

Vores tæller er synkron. Det er næsten ingen rigtige apps. Det er vores erfaring, at datahentning er den hyppigste årsag til, at et React Native-team overhovedet vælger Redux: en forespørgsel har en indlæsningstilstand, en succes-tilstand og en fejl-tilstand, flere skærme har brug for alle tre, og det holder hurtigt op med at fungere at sende dem rundt som props.

Redux Toolkit tilbyder to svar, og valget mellem dem er den praktiske beslutning, de fleste teams står overfor.

createAsyncThunk omslutter et promise og dispatchet tre actions for dig, én pr. fase, som du håndterer i extraReducers:

// redux/counterSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

export const fetchCounter = createAsyncThunk(
  'counter/fetch',
  async (userId) => {
    const response = await fetch(`https://api.example.com/counter/${userId}`);
    return response.json();
  },
);

const counterSlice = createSlice({
  name: 'counter',
  initialState: { amount: 0, status: 'idle', error: null },
  reducers: {
    increment: (state) => {
      state.amount += 1;
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchCounter.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchCounter.fulfilled, (state, action) => {
        state.status = 'succeeded';
        state.amount = action.payload.amount;
      })
      .addCase(fetchCounter.rejected, (state, action) => {
        state.status = 'failed';
        state.error = action.error.message;
      });
  },
});

Komponenten dispatchet thunken og læser status:

const dispatch = useDispatch();
const { amount, status } = useSelector((state) => state.counter);

useEffect(() => {
  if (status === 'idle') dispatch(fetchCounter(userId));
}, [status, dispatch, userId]);

RTK Query går skridtet videre. Definer dine endpoints, så genererer det thunks, reducere, cachen og en hook pr. endpoint, så al den håndtering af indlæsning og fejl forsvinder ind i hooken:

// redux/api.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://api.example.com/' }),
  endpoints: (builder) => ({
    getCounter: builder.query({ query: (userId) => `counter/${userId}` }),
  }),
});

export const { useGetCounterQuery } = api;

const { data, isLoading, error } = useGetCounterQuery(userId);

Så hvilken skal du vælge? Brug createAsyncThunk når det asynkrone arbejde er en sideeffekt, du selv styrer, såsom et login-forløb eller en baggrundssynkronisering. Brug RTK Query, når du cacher serverdata, hvilket på mobil er det meste af tiden, da caching, genhentning og validering netop er de dele, teams ofte gør forkert, når de gør det manuelt. Mark Erikson, en af Redux-vedligeholderne, forklarer hvorfor thunks er standardværktøjet til asynkront arbejde i denne artikel om RTK og asynkron logik.

Vores guide til hvordan man håndterer asynkrone operationer med Redux dykker ned i thunks, sagas og middleware-alternativerne. For et produktionseksempel på det samme problem med indlæsning, caching og genhentning, Confinze kombinerer Redux med React Query i et fintech-produkt og opnåede en fastholdelsesrate på 85 % takket være denne forudsigelighed.

blue arrow to the left
Imaginary Cloud logo

Testen med de fire spørgsmål til state

Redux er ikke gratis. Det tilføjer filer, indirektion og et mønster, som alle udviklere på teamet skal lære. Før vi tager det i brug på et projekt, kører vi testen med de fire spørgsmål til state, og svarene afgør som regel sagen:

  1. Bliver den samme state læst på mere end to skærme? Godkendelse, brugerprofil, indkøbskurv, feature flags. Hvis state sendes ned gennem mere end to niveauer eller duplikeres på tværs af skærme, tjener et store sig selv hjem. Hvis det er en formular, der lever og dør på én skærm, useState er det rigtige værktøj.
  2. Ændrer staten sig fra mere end ét sted? En værdi, der skrives af en skærm, en push-besked og en baggrundssynkronisering, er præcis, hvad et centraliseret store er til for. En værdi, der kun skrives af den skærm, der viser den, er det ikke.
  3. Har appen brug for, at staten overlever en genstart? Tokens, onboarding-fremskridt og offline-kladder skubber dig mod et store plus redux-persist, fordi alternativet er spredte AsyncStorage-kald, som ingen kan revidere.
  4. Vil mere end én eller to udviklere arbejde på appen i løbet af det næste år? Redux' virkelige afkast er læsbarhed. Nye medarbejdere kan læse hver eneste state-overgang i én mappe i stedet for at skulle spore props gennem komponenttræet.
Flowchart over en test med 4 spørgsmål til at bestemme tilstandsstyring i React Native Redux.

To eller flere ja-svar, og Redux er pengene værd. Ét eller ingen, og Reacts egen state plus Context vil tjene dig bedre til en lavere pris.

Vi har prøvet begge dele. Et internt værktøj med én skærm og én udvikler forblev på useState og blev leveret hurtigere af den grund. En leverings-app med offline-kladder, push-opdateringer og et roterende team blev skrevet om til Redux Toolkit, netop fordi fejl i staten konstant opstod fra tre retninger på én gang. På AppTweakvar disciplineret state-styring i et datatungt dashboard en del af årsagen til, at vi skar indlæsningstiden ned med 80%, hvilket er den gevinst, denne test i virkeligheden vejer.

Den kommercielle ramme gør sig gældende i begge tilfælde. Du afvejer en lille, fast opstartsomkostning mod omkostningerne ved ændringer senere: hvor hurtigt en ny udvikler bliver produktiv, hvor trygt du kan frigive en funktion, der berører delte data, og hvor stor en del af dit budget, der går til at genskabe fejl frem for at bygge nyt. Du kan læse mere om disse resultater i vores cases.

blue arrow to the left
Imaginary Cloud logo

Ofte stillede spørgsmål

Har man stadig brug for Redux i React Native?

Ikke altid. Siden introduktionen af hooks og Context kan mange apps håndtere delt tilstand uden. Redux er stadig relevant, når tilstand skal læses og skrives fra flere skærme, skal overleve en genstart, eller vedligeholdes af et team frem for en enkelt udvikler. Til en enkelt skærm med lokal tilstand er useState rigeligt.

Redux Toolkit eller Context API: hvad skal jeg vælge?

De løser forskellige problemer. Context sender en værdi ned gennem træet og genindlæser alle forbrugere, når den ændres, hvilket fungerer fint til temaer eller sprogindstillinger. Redux Toolkit tilføjer forudsigelige opdateringer, selektive genindlæsninger via selectors, tidsrejse-debugging og et dokumenteret sted for enhver tilstandsændring. Vælg Context til værdier, der sjældent ændres eller er få, og Redux Toolkit til tilstand, der ændres ofte eller kommer fra flere kilder.

Hvordan gemmer jeg Redux-tilstand permanent i React Native?

Installer redux-persist og AsyncStorage, pak din reducer ind med persistReducer og en persistConfig, opret persistor med persistStore, og pak derefter din app ind i PersistGate så brugerfladen venter på den gemte tilstand. Brug whitelist- og blacklist-indstillingerne til kun at gemme de dele, der har brug for det, og opbevar tokens i sikker lagring frem for AsyncStorage, som ikke er krypteret.

Hvordan løser jeg fejlen 'A non-serializable value was detected in an action'?

Den stammer fra configureStore's serializable-tjek møder redux-persists egne handlinger. Send en middleware-indstilling til configureStore og tilføj FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE og REGISTER til ignoredActions. Deaktiver ikke tjekket helt, da du ellers mister advarslen for værdier, der reelt ikke er serialiserbare i din egen kode.

Bør jeg bruge createAsyncThunk eller RTK Query til API-kald?

RTK Query til serverdata, som du ønsker cachet, hentet på ny og invalideret, hvilket dækker de fleste mobilskærme. createAsyncThunk til sideeffekter, som du selv styrer fra start til slut, såsom et login-flow eller et synkroniseringsjob. Begge følger med i Redux Toolkit, så valget træffes fra sag til sag frem for på projektniveau.

Er Redux for omfattende til en lille mobilapp?

Ofte ja. Hvis appen kun har en håndfuld skærme, én udvikler og ingen tilstand, der skal overleve en session, koster boilerplate-koden mere, end den gavner. Prøv at køre de fire spørgsmål om tilstand herover: Hvis du har færre end to ja-svar, er du bedre tjent med useState og Context.

Skal jeg bruge createStore, eller er Redux Toolkit nok?

Redux Toolkit er nok, og det er, hvad Redux-teamet anbefaler til nye projekter. Den centrale createStore er nu forældet. configureStore, createSlice og Immer erstatter de manuelle handlingskonstanter, switch-statement-reducers og den opsætning af store, der blev vist tidligere i denne guide. Den længere form er stadig værd at læse én gang, da den viser, hvad værktøjskassen gør for dig, og det er, hvad du vil støde på i ældre kodebaser.

blue arrow to the left
Imaginary Cloud logo

Kort fortalt

Actions beskriver, hvad der skete, reducers bestemmer, hvordan tilstanden ændrer sig, og storen gemmer resultatet, så alle skærme kan tilgå det. Redux Toolkit fjerner det meste af boilerplate-koden, RTK Query håndterer serverdata, redux-persist bevarer tilstanden på tværs af genstarter, og vores test med fire spørgsmål hjælper dig med at vurdere, om det er værd at implementere. Da Redux er uafhængig af visningslaget, kan alt dette også overføres til en React-webapp.

Overvejer du tilstandshåndtering til et mobilprodukt, eller har du overtaget en app, hvor tilstandshåndteringen er blevet en flaskehals? Vores team arbejder med dette hver dag. Tag et kig på vores udviklingsydelser, eller fortæl os om dit projekt , så giver vi dig en ærlig vurdering af, om Redux er det rette valg for dig.

Banneret "Udfør en UX-audit" viser en blå smartphone med app-brugerflader i lag og knappen "Tal med os".
blue arrow to the left
Imaginary Cloud logo
Tiago Madeira
Tiago Madeira

Datalogstuderende og ImaginaryCloud deltidstimer. Ivrig efter at lære nye teknologier og teknikker. Tennis og klaverspiller.

Read more posts by this author
Alexandra Mendes
Alexandra Mendes

Alexandra Mendes er Senior Growth Specialist hos Imaginary Cloud med 3+ års erfaring med at skrive om softwareudvikling, AI og digital transformation. Efter at have gennemført et frontend-udviklingskursus fik Alexandra nogle praktiske kodningsevner og arbejder nu tæt sammen med tekniske teams. Alexandra brænder for, hvordan nye teknologier former erhvervslivet og samfundet, og hun nyder at omdanne komplekse emner til klart og nyttigt indhold for beslutningstagere.

LinkedIn

Read more posts by this author

People who read this post, also found these interesting:

Dropdown caret icon