Dodatkowe reducery w Redux

Otwórzmy naszą aplikację z produktami, a w niej plik productsSlice.js. Prawdopodobnie zauważyłeś, że tworzyliśmy thunka fetchProducts jako oddzielną funkcję? Zrobiliśmy tak, ponieważ createSlice nie obsługuje definiowania thunków. Jak w takim razie sprawić, aby reducer slice'a products reagował na akcje, które są zdefiniowane poza products? W końcu potrzebujemy przetworzyć akcje, które wysyła thunk fetchProducts.

W takich przypadkach createSlice ma właściwość extraReducers, która pozwala dodawać dodatkowe reducery, które z kolei będą przetwarzać akcje zdefiniowane nie w tym slice'u.

Dodajmy teraz w ciele funkcji createSlice po właściwości reducers ze wszystkimi reducerami kolejną metodę extraReducers:

products/productsSlice.jsextraReducers() {},

Musimy przekazać jej obiekt builder, który ma metody, za pomocą których można dodać dodatkowe reducery:

products/productsSlice.jsextraReducers(builder) {},

Będziemy używać jednej z metod builder - addCase, która przyjmuje pierwszym parametrem action creator, a drugim reducer. Jedna z akcji, którą będzie nam wysyłać fetchProducts przy żądaniu to fetchProducts.pending, co mówi nam o tym, że żądanie zostało wysłane. W tym przypadku zmieńmy status na 'in progress' (wcześniej był 'idle'):

products/productsSlice.jsextraReducers(builder) {
  builder.addCase(fetchProducts.pending, (state) => {
    state.status = 'in progress'
  })
},

Teraz przetwórzmy akcję, która wyśle się w przypadku udanego żądania. Tutaj będziemy zmieniać nie tylko status w stanie na 'success', ale także zabierzemy produkty do slice'a products z payload akcji. Aby zebrać wszystkie produkty, przyda nam się metoda concat:

products/productsSlice.jsextraReducers(builder) {
  builder
    .addCase(fetchProducts.pending, (state) => {
      state.status = 'in progress'
    })
    .addCase(fetchProducts.fulfilled, (state, action) => {
      state.status = 'success'
      state.products = state.products.concat(action.payload)
    })
},

W przypadku nieudanego żądania zmienimy status na 'fail' i zapiszemy w stanie komunikat o błędzie:

products/productsSlice.js.addCase(fetchProducts.rejected, (state, action) => {
  state.status = 'fail'
  state.error = action.error.message
})

Jeśli teraz uruchomimy naszą aplikację i w menu klikniemy na 'Products', to po kilku sekundach (pamiętasz o opóźnieniu, które ustawiliśmy na serwerze?) zobaczymy listę z produktami.

Tak samo zobaczymy zmiany w Redux DevTools. Teraz nasze produkty pojawią się i w stanie (spójrz na zakładkę 'State'), jeśli kliknąłeś na akcję products/fetchProducts/fulfilled. Pojawi się tam też nowy status 'success'. Kliknij teraz na akcję products/fetchProducts/pending i spójrz, czym teraz różni się zakładka 'State'.

Jedyna nieprzyjemna rzecz, która może ci się przytrafić (mówiłem o tym na poprzedniej lekcji) - to duplikowanie żądania danych. W wyniku czego, u nas na liście otrzymamy nie 8, a całe 16 produktów i krytyczne ostrzeżenia w konsoli deweloperskiej. Zajmijmy się tym na następnej lekcji.

Задание 1

Otwórz twoją aplikację ze studentami. Otwórz w niej plik studentsSlice.js. W ciele funkcji createSlice po właściwości reducers dodaj właściwość extraReducers. Przekaż metodzie extraReducers builder.

Задание 2

Za pomocą metody builder.addCase dodaj obsługę dla akcji pending, fulfilled i rejected, które wysyła fetchStudents przy żądaniu danych, jak pokazano na lekcji.