reactjs - Reducers vs ExtraReducers som ett sätt att hantera thunks


Jag håller för närvarande på att konvertera en gammal Redux-kodbas till att använda Redux-Toolkit (RTK), och jag verkar inte kunna förstå skillnaden mellan dessa tillvägagångssätt. Tänk på denna slice:

interface MyState {
  someItems: string[];
}

const myInitialState: MyState = {
  someItems: []
};

const mySlice = createSlice({
  name: 'mySlice',
  initialState: myInitialState,
  reducers: {
    setItems(state, action: PayloadAction<string[]>) {
      state.someItems = action.payload;
    }
  },
  extraReducers: builder => {
    builder.addCase(fetchItems.fulfilled, (state, action) => {
      state.someItems = action.payload;
    });
  }
});

Mitt traditionella sätt att använda redux har varit att kalla på reducer direkt från en Thunk, men jag ser att sättet som definieras i extraReducers också är möjligt.

const fetchItems = createAppAsyncThunk(
  'mySlice/fetchItems',
  async (_, thunkAPI) => {
    try {
      //Get data from backend
      const response = await getItems();

      //Option 1
      thunkAPI.dispatch(mySlice.actions.setItems(response));
      //Option 2
      return response;
    } catch (e) {
      return thunkAPI.rejectWithValue('failed');
    }
  }
);

Vad är skillnaden mellan dessa tillvägagångssätt? Är någon av dem att föredra framför den andra? Slutligen, när jag gör ändringar måste jag ofta ladda om data för de senaste uppdateringarna. Den här gången tror jag att jag måste använda dispatch inne i Thunk. Eller finns det alternativ?

const editItem = createAppAsyncThunk<void, string>(
  'mySlice/fetchItems',
  async (changedValue, thunkAPI) => {
    try {
      //Send data to backend
      await sendSomethingToBackend(changedValue);

      thunkAPI.dispatch(fetchItems());
    } catch (e) {
      return thunkAPI.rejectWithValue('failed');
    }
  }
);

Svar #1

Givet :

const fetchItems = createAppAsyncThunk(
  'mySlice/fetchItems',
  async (_, thunkAPI) => {
    try {
      //Get data from backend
      const response = await getItems();

      // Option 1
      thunkAPI.dispatch(mySlice.actions.setItems(response));
      // Option 2
      return response;
    } catch (e) {
      thunkAPI.rejectWithValue('failed');
    }
  }
);

Vad är skillnaden mellan dessa tillvägagångssätt?

I det stora hela finns det inte mycket skillnad mellan de två när det gäller det enskilda mySlice-tillståndssegmentet. Den största skillnaden handlar om vad, eller var, som kan hantera de "lösta" thunks.

  • Skicka en explicit åtgärd: thunkAPI.dispatch(mySlice.actions.setItems(response));

    • Endast mySlice setItems-reducerfunktionen kan svara på och hantera denna åtgärd.
  • Skicka en implicit uppfylld (fulfilled) åtgärd: return response;

    • Alla Redux-Toolkit (RTK) slice's extraReducers-funktioner kan reagera på och hantera fetchItems.fulfilled-åtgärden.

Föredras någon av dem över den andra?

Detta är subjektivt, men att använda Thunks .pending, .fulfilled och .rejected åtgärder är troligen den allmänt accepterade föredragna metoden eftersom det ger större flexibilitet. Ett av de främsta målen med RTK var att minska mängden kod du behövde skriva själv. Att använda de automatiskt genererade Thunk-åtgärderna är en del av detta mål.

const editItem = createAppAsyncThunk<void, string>(
  'mySlice/fetchItems',
  async (changedValue, thunkAPI) => {
    try {
      //Send data to backend
      await sendSomethingToBackend(changedValue);

      thunkAPI.dispatch(fetchItems());
    } catch (e) {
      thunkAPI.rejectWithValue('failed');
    }
  }
);

Slutligen, när jag gör ändringar måste jag ofta ladda om datan för de senaste uppdateringarna. Den här gången tror jag att jag MÅSTE använda dispatch inuti thunk.

Ja, här skulle du absolut behöva skicka fetchItems-åtgärden för att hämta data igen som sendSomethingToBackend kan ha uppdaterat i backend.

Eller finns det alternativ?

Om du hämtar och uppdaterar data kan ett alternativ för dig vara att använda Redux-Toolkit Query (RTK Query). Du skulle skapa en API-slice som hanterar queries och mutations. Konvertera fetchItems-Thunk till en query, och editItem-Thunk till en mutation, och genom att använda cache-taggar kan mutationerna ogiltigförklara frågade data och automatiskt trigga queries att hämta om. Med andra ord, ytterligare minskning av koden du skriver.