angular - Reducers och oföränderlighet i ngrx


När man skapar en reducer-funktion i ngrx, säger all information jag läser att jag ska returnera en kopia av det ursprungliga/föregående tillståndet. Antingen genom att använda spridningsoperatorer eller genom att använda ett bibliotek eller tricks som JSON.parse(JSON.stringify(state)).

Men jag hittade en hake där och jag kunde inte hitta någon som pratade om det. Det sista tillståndet som returneras i en reducer är det tillstånd som kommer att delas med alla nuvarande prenumeranter och med framtida prenumeranter också. Det betyder att alla komponenter som använder en viss store kommer att se samma tillståndsobjekt.

Det betyder också att om något värde i tillståndet ändras i en komponent (utan att skicka en åtgärd), kommer lagret faktiskt att ha det modifierade värdet, men de andra komponenterna kommer inte att meddelas. Vad är poängen med att returnera en kopia av det aktuella tillståndet om det ska delas överallt?

Ordet immutable används hela tiden, men det tillståndet är inte alls immutable, eftersom lagret returnerar sitt eget inre objekt och inte en kopia av det.

Jag förstår om den oföränderliga delen är ett koncept som utvecklaren behöver följa. Men då behöver kopian av det ursprungliga objektet/värdena göras i komponenten som använder det. Att returnera en ytlig eller djup kopia från reducern verkar bara vara ett slöseri med processorkraft och minne.

Svar #1

Jag ska försöka svara.

En reducer i pseudokod ser ut så här:

myReducer(state, action) {

  switch(action) {

    case ACTION_1:
      return {...state, prop: action.payload}

    case ACTION_2:
      const newState = _.cloneDeep(state)
      newState.prop = action.payload
      return newState

    default:
      return state
  }
}

I fall ACTION_1 ändrar du inte tillståndet. Spread-operatören skapar ett nytt objekt med en ny referens och den nya referensen är vad som behövs för att signalera en förändring.

I fall ACTION_2 klonar du tillståndet. Du ändrar det klonade tillståndet och returnerar det. Eftersom det klonade tillståndet har en ny objektreferens signalerar det att en ändring har gjorts och alla är nöjda.

I standardscenario ignoreras alla andra åtgärder (t.ex. ACTION_3) och det ursprungliga tillståndet returneras vilket innebär att tillståndet inte har förändrats. Objektreferensen har inte förändrats och därför signaleras "ingen förändring" (detta är anledningen till att det är viktigt att inte ändra det ursprungliga tillståndet).

När en åtgärd utlöses, skickas åtgärden till ALLA reduktorer. Och därför kan reduktorer som inte vill ändra sin tillhörande del av tillståndet ignorera åtgärden genom att förlita sig på standardfall-satsen.

En enda handling kan, och gör ofta, utlösa tillståndsförändringar i flera reducerare.

Om den returnerade objektreferensen har ändrats, kommer det att utlösa alla relaterade RxJS-tillståndsabonnemang för den specifika delen av tillståndet i fråga. Vilka abonnemang som utlöses kan minimeras genom att använda några bra ngrx-selektorer.

PS Det finns ett fantastiskt bibliotek som heter ngrx-store-freeze som kommer att upprätthålla principen om 'ingen mutation'. Det kommer att ge ett fel tidigt om du muterar tillståndet. Detta hjälper till att undvika svårspårade buggar. Koppla in store freeze med en meta-reducer.

PPS Hela syftet med att använda objektreferensen för att avgöra förändring är att det är mycket snabbare att kontrollera en objektreferens än att kontrollera varje värde på ett objekt för att se om det har ändrats. Detta är anledningen till att oföränderlighet är så relevant.

Relaterade artiklar