Дадатковыя рэдʼюсеры ў Redux

Давайце адчынём наша прыкладанне з прадуктамі, а ў ім файл productsSlice.js. Напэўна вы заўважылі, што мы стваралі thunk fetchProducts як асобную функцыю? Мы зрабілі так, таму што createSlice не падтрымлівае вызначэнне thunks. Як жа нам у гэтым выпадку прымусіць reducer слайса products адказваць на экшэны, якія вызначаны за межамі products? Бо нам як раз трэба апрацаваць экшэны, якія пасылае thunk fetchProducts.

Для такіх выпадкаў у createSlice ёсць ўласцівасць extraReducers, якое дазваляе дадаваць дадатковыя рэдʼюсеры, якія ў сваю чаргу і будуць апрацоўваць экшэны, вызначаныя не ў даным слайсе.

Давайце цяпер у целе функцыі createSlice пасля ўласцівасці reducers з усімі рэдʼюсерамі дадамо яшчэ адзін метад extraReducers:

products/productsSlice.jsextraReducers() {},

Гэтаму метаду мы павінны перадаць аб'ект builder, у якога ёсць метады, з дапамогай якіх можна дадаць дадатковыя рэдʼюсеры:

products/productsSlice.jsextraReducers(builder) {},

Мы будзем выкарыстоўваць адзін з метадаў builder - addCase, які першым параметрам прымае action creator, а другім reducer. Адзін з экшэнаў, які нам будзе адпраўляць fetchProducts пры запыце гэта fetchProducts.pending, што кажа нам аб тым, што запыт адпраўлены. Давайце ў гэтым выпадку будзем мяняць статус на 'in progress' (да гэтага ён быў 'idle'):

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

Цяпер апрацуем экшэн, які адправіцца ў выпадку паспяховага запыту. Тут мы будзем мяняць не толькі статус у стэйце на 'success', але і забярэм прадукты ў слайс products з payload экшэна. Каб сабраць усе прадукты нам спатрэбіцца метад 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)
    })
},

У выпадку няўдалага запыту мы памяняем статус на 'fail' і запiшам у стэйт паведамленне пра памылку:

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

Калі мы зараз запусцім наша прыкладанне і ў меню клікнем па 'Products', то праз пару секунд (памятаеце пра затрымку, якую мы выставілі на серверы?) убачым спіс з прадуктамі.

Дакладна гэтак жа мы ўбачым змены і ў Redux DevTools. Цяпер нашы прадукты з'явяцца і у стэйце (паглядзіце ўкладку 'State'), калі вы клікнулі па экшэне products/fetchProducts/fulfilled. Там жа з'явіцца і новы статус 'success'. Клікніце цяпер на экшэн products/fetchProducts/pending і паглядзіце, чым цяпер адрозніваецца ўкладка 'State'.

Адзін непрыемны момант, які у вас можа здарыцца (я казаў пра гэта на папярэднім уроке) - гэта дубляванне запыту даных. У выніку чаго, у нас у спісе атрымаецца не 8, а цэлых 16 прадуктаў і крытычныя папярэджанні у кансолі распрацоўніка. Давайце разбярэмся з гэтым на наступным уроке.

Задание 1

Адчынiце ваша прыкладанне са студэнтамі. Адчынiце ў ім файл studentsSlice.js. У целе функцыі createSlice пасля ўласцівасці reducers дадайце ўласцівасць extraReducers. Перадайце метаду extraReducers builder.

Задание 2

З дапамогай метаду builder.addCase дадайце апрацоўку для экшэнаў pending, fulfilled і rejected, якія адпраўляе fetchStudents пры запыце даных, як паказана ў уроку.