Hozirda React framewrokga asoslangan veb-ilovalarning State management ishlab chiqish Redux kutubxonasi yordamida amalga oshirilmoqda. Ushbu kutubxona FLUX arxitekturasining eng mashhur amalga oshirilishi hisoblanadi va bir qator aniq afzalliklarga qaramay, juda muhim kamchiliklarga ega, masalan:
- kodni yozish va tartibga solish uchun tavsiya etilgan naqshlarning murakkabligi.
- asenkron xatti-harakatlar va nojo'ya ta'sirlarni boshqarish vositalarining etishmasligi, bu uchinchi tomon ishlab chiquvchilari tomonidan yozilgan ko'plab qo'shimchalardan mos vositani tanlash zarurligiga olib keladi.
Ushbu kamchiliklarni bartaraf etish uchun Redux ishlab chiquvchilari Redux Toolkit kutubxonasini taqdim etdilar. Ushbu vosita Redux yordamida ilovalarni ishlab chiqishni soddalashtirish uchun mo'ljallangan amaliy echimlar va usullar to'plamidir. Ushbu kutubxonani ishlab chiquvchilar Redux-dan foydalanishning odatiy holatlarini soddalashtirishni maqsad qilishgan. Ushbu vosita Redux-dan foydalanishning har bir mumkin bo'lgan holatida universal echim emas, lekin ishlab chiquvchi yozishi kerak bo'lgan kodni soddalashtirishga imkon beradi.
Ushbu maqolada biz Redux Toolkit-ga kiritilgan asosiy vositalar haqida gaplashamiz, shuningdek, ichki dasturimizning bir qismi misolida ularni mavjud kodda qanday ishlatishni ko'rsatamiz.
Bepul ReactJS va Redux kurimizni ko'rishingiz mumkin.
Redux-Toolkit kutubxona haqida qisqacha
- Chiqarilishidan oldin kutubxona redux-starter-kit deb nomlangan;
- reliz 2019 yil oktyabr oyining oxirida bo'lib o'tdi;
- kutubxona rasmiy ravishda Redux ishlab chiquvchilari tomonidan qo'llab-quvvatlanadi.
Ishlab chiquvchilarning bayonotiga ko'ra, Redux Toolkit quyidagi funktsiyalarni bajaradi:
- Redux - dan tezda foydalanishni boshlashga yordam beradi;
- Oddiy vazifalar va Redux kodi bilan ishlashni osonlashtiradi;
- Redux-ning eng yaxshi standart amaliyotlaridan foydalanishga imkon beradi;
- Shablon kodi bo'lgan ishonchsizlikni kamaytiradigan echimlarni taklif qiladi.
Redux Toolkit maxsus ishlab chiqilgan to'plamni taqdim etadi va odatda Redux bilan birgalikda ishlatiladigan bir qator yaxshi tasdiqlangan vositalarni qo'shadi. Ushbu yondashuv ishlab chiquvchiga o'z ilovasida qanday va qanday vositalardan foydalanishni hal qilish imkonini beradi. Ushbu maqola davomida biz ushbu kutubxona qanday qarzlardan foydalanishini ta'kidlaymiz. To'liqroq ma'lumot va Redux Toolkit bog'liqliklarini @reduxjs/toolkit paket tavsifidan olish mumkin.
Redux Toolkit kutubxonasi tomonidan taqdim etilgan eng muhim xususiyatlar:
- configureStore — saqlash yaratish va sozlash jarayonini soddalashtirishga mo'ljallangan xususiyat;
- createReducer — reduserni qisqacha va tushunarli tarzda tasvirlash va yaratishga yordam beradigan funktsiya;
- createAction — berilgan harakat turi qatori uchun harakat yaratuvchisi funktsiyasini qaytaradi;
- createSlice — createaction va Create Reducer funksiyalarini birlashtiradi;
- createSelector — Reselect kutubxonasidagi funksiya foydalanish qulayligi uchun haddan tashqari ko'p.
Shuni ham ta'kidlash kerakki, Redux Toolkit TypeScript bilan to'liq birlashtirilgan. Bu haqda ko'proq ma'lumotni rasmiy hujjatlarning TypeScript bilan foydalanish bo'limidan olishingiz mumkin.
Foydalanish
Redux Toolkit kutubxonasidan haqiqatan ham ishlatiladigan React Redux dasturining bir qismi misolida foydalanishni ko'rib chiqing.
Eslatma. Maqolada Redux Toolkit-dan foydalanmasdan ham, ushbu kutubxonadan foydalanishning ijobiy va salbiy tomonlarini yaxshiroq baholashga imkon beradigan manba kodi keltirilgan.
Vazifa
Bizning ichki ilovalarimizdan birida biz ishlab chiqaradigan dasturiy mahsulotlarning reliz ma'lumotlarini qo'shish, tahrirlash va ko'rsatish zarurati paydo bo'ldi. Ushbu harakatlarning har biri uchun alohida API funktsiyalari ishlab chiqilgan, ularning natijalari Redux do'koniga qo'shilishi kerak. Asenkron xatti-harakatlar va yon ta'sirlarni boshqarish vositasi sifatida biz Thunk-dan foydalanamiz.
Store yaratish
State (ombor) yaratishni amalga oshiradigan manba kodining dastlabki versiyasi quyidagicha edi:
import {
createStore, applyMiddleware, combineReducers, compose,
} from 'redux';
import thunk from 'redux-thunk';
import * as reducers from './reducers';
const ext = window.__REDUX_DEVTOOLS_EXTENSION__;
const devtoolMiddleware =
ext && process.env.NODE_ENV === 'development' ? ext() : f => f;
const store = createStore(
combineReducers({
...reducers,
}),
compose(
applyMiddleware(thunk),
devtoolMiddleware
)
);
Agar siz berilgan kodni diqqat bilan ko'rib chiqsangiz, bajarilishi kerak bo'lgan juda uzoq harakatlar ketma-ketligini ko'rishingiz mumkin saqlash to'liq tuzilgan bo'lishi uchun. Redux Toolkit ushbu protsedurani soddalashtirish uchun mo'ljallangan vositani o'z ichiga oladi, ya'ni: configureStore funktsiyasi.
Funktsiyasi configureStore
Ushbu vosita sizga reduserlarni avtomatik ravishda birlashtirish, Redux midlvarlarini qo'shish (sukut bo'yicha redux-thunk-ni o'z ichiga oladi), shuningdek Redux DevTools kengaytmasidan foydalanish imkonini beradi. Kirish parametrlari sifatida configureStore funktsiyasi quyidagi xususiyatlarga ega ob'ektni oladi:
- reducer — maxsus reduserlar to'plami,
- middleware — omborga ulanish uchun mo'ljallangan o'rta vars massivini belgilaydigan ixtiyoriy parametr,
- devTools — brauzerga o'rnatilgan Redux DevTools kengaytmasini yoqish uchun mantiqiy turdagi parametr (standart qiymat rost),
- preloadedState — dastlabki saqlash holatini belgilaydigan ixtiyoriy parametr,
- enhancers — kuchaytirgichlar to'plamini belgilaydigan ixtiyoriy parametr.
O'rta bo'limlarning eng mashhur ro'yxatini olish uchun siz Redux Toolkit-ning bir qismi bo'lgan getDefaultMiddleware-ning maxsus funktsiyasidan foydalanishingiz mumkin. Ushbu funktsiya sukut bo'yicha Redux Toolkit kutubxonasiga kiritilgan midlvarlar bilan qatorni qaytaradi. Ushbu o'rta varlarning ro'yxati sizning kodingiz qaysi rejimda ishlashiga qarab farq qiladi. Ishlab chiqarish rejimida massiv faqat bitta elementdan iborat — thunk. Rivojlanish rejimida ushbu yozuv paytida ro'yxat quyidagi midlvarlar bilan to'ldiriladi:
- serializableStateInvariant — Redux Toolkit-da foydalanish uchun maxsus ishlab chiqilgan va oddiy JS ma'lumotlari bo'lmagan funktsiyalar, Promise, Symbol va boshqa qiymatlar kabi ketma-ket bo'lmagan qiymatlar uchun davlat daraxtini tekshirish uchun mo'ljallangan vosita;
- immutableStateInvariant — Redux-immutable-state-invariant paketidagi midlvar, ombordagi ma'lumotlar mutatsiyalarini aniqlash uchun mo'ljallangan.
Midlvarlarning qaytish ro'yxatini belgilash uchun getDefaultMidlleware funksiyasi kiritilgan midlvarlar ro'yxatini va ularning har biri uchun sozlamalarni belgilaydigan obyektni qabul qiladi. Ushbu ma'lumot haqida ko'proq ma'lumotni rasmiy hujjatlarning tegishli qismida topishingiz mumkin.
Endi yuqorida tavsiflangan vositalardan foydalanib, omborni yaratish uchun mas'ul bo'lgan kod qismini qayta yozamiz. Natijada biz quyidagilarni olamiz:
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import * as reducers from './reducers';
const middleware = getDefaultMiddleware({
immutableCheck: false,
serializableCheck: false,
thunk: true,
});
export const store = configureStore({
reducer: { ...reducers },
middleware,
devTools: process.env.NODE_ENV !== 'production',
});
Kodning ushbu qismi misolida configreStore funktsiyasi quyidagi muammolarni hal qilishini aniq ko'rish mumkin:
- avtomatik ravishda combineReducers-ni chaqirib, reducerlarni birlashtirish zarurati,
- applymiddleware-ni avtomatik ravishda chaqirish orqali midlvarlarni birlashtirish zarurati.
Shuningdek, redux-devtools-extension paketidagi composeWithDevTools xususiyatidan foydalanib, Redux DevTools kengaytmasini yanada qulayroq yoqish imkonini beradi. Yuqorida aytilganlarning barchasi ushbu funktsiyadan foydalanish kodni yanada ixcham va tushunarli qilish imkonini beradi.
Reducer
Endi Redux Toolkit-ning harakatlarni ishlab chiqish, harakat yaratuvchilari va reduser-ning imkoniyatlarini ko'rib chiqamiz. Redux Toolkit-dan foydalanmasdan kodning dastlabki versiyasi actions fayllari sifatida tashkil etilgan.js va reducers.js. Actions faylining tarkibi.js quyidagicha ko'rinardi:
import * as productReleasesService from '../../services/productReleases';
export const PRODUCT_RELEASES_FETCHING = 'PRODUCT_RELEASES_FETCHING';
export const PRODUCT_RELEASES_FETCHED = 'PRODUCT_RELEASES_FETCHED';
export const PRODUCT_RELEASES_FETCHING_ERROR =
'PRODUCT_RELEASES_FETCHING_ERROR';
…
export const PRODUCT_RELEASE_UPDATING = 'PRODUCT_RELEASE_UPDATING';
export const PRODUCT_RELEASE_UPDATED = 'PRODUCT_RELEASE_UPDATED';
export const PRODUCT_RELEASE_CREATING_UPDATING_ERROR =
'PRODUCT_RELEASE_CREATING_UPDATING_ERROR';
function productReleasesFetching() {
return {
type: PRODUCT_RELEASES_FETCHING
};
}
function productReleasesFetched(productReleases) {
return {
type: PRODUCT_RELEASES_FETCHED,
productReleases
};
}
function productReleasesFetchingError(error) {
return {
type: PRODUCT_RELEASES_FETCHING_ERROR,
error
}
}
…
export function fetchProductReleases() {
return dispatch => {
dispatch(productReleasesFetching());
return productReleasesService.getProductReleases().then(
productReleases => dispatch(productReleasesFetched(productReleases))
).catch(error => {
error.clientMessage = "Can't get product releases";
dispatch(productReleasesFetchingError(error))
});
}
}
…
export function updateProductRelease(
id, productName, productVersion, releaseDate
) {
return dispatch => {
dispatch(productReleaseUpdating());
return productReleasesService.updateProductRelease(
id, productName, productVersion, releaseDate
).then(
productRelease => dispatch(productReleaseUpdated(productRelease))
).catch(error => {
error.clientMessage = "Can't update product releases";
dispatch(productReleaseCreatingUpdatingError(error))
});
}
}
Reducers faylining tarkibi.Redux Toolkit-dan foydalanishdan oldin js:
const initialState = {
productReleases: [],
loadedProductRelease: null,
fetchingState: 'none',
creatingState: 'none',
updatingState: 'none',
error: null,
};
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case productReleases.PRODUCT_RELEASES_FETCHING:
return {
...state,
fetchingState: 'requesting',
error: null,
};
case productReleases.PRODUCT_RELEASES_FETCHED:
return {
...state,
productReleases: action.productReleases,
fetchingState: 'success',
};
case productReleases.PRODUCT_RELEASES_FETCHING_ERROR:
return {
...state,
fetchingState: 'failed',
error: action.error
};
…
case productReleases.PRODUCT_RELEASE_UPDATING:
return {
...state,
updatingState: 'requesting',
error: null,
};
case productReleases.PRODUCT_RELEASE_UPDATED:
return {
...state,
updatingState: 'success',
productReleases: state.productReleases.map(productRelease => {
if (productRelease.id === action.productRelease.id)
return action.productRelease;
return productRelease;
})
};
case productReleases.PRODUCT_RELEASE_UPDATING_ERROR:
return {
...state,
updatingState: 'failed',
error: action.error
};
default:
return state;
}
}
Ko'rib turganimizdek, bu erda Shablon kod qismi mavjud: harakat turlarining konstantalari, harakatlar yaratuvchilari, yana doimiylar, ammo allaqachon reduser kodida ushbu kodning barchasini yozish uchun vaqt sarflashingiz kerak. Agar siz Redux Toolkit-ning bir qismi bo'lgan createAction va createReducer funktsiyalaridan foydalansangiz, ushbu shablon kodida qisman xalos bo'lishingiz mumkin.
Fukntsiya createReducer
Endi reduserni ko'rib chiqamiz. Bizning misolimizda bo'lgani kabi, reducerlar ko'pincha switch operatori yordamida amalga oshiriladi, har bir qayta ishlangan harakat turi uchun bitta registr mavjud. Ushbu yondashuv yaxshi ishlaydi, ammo shablon kodi yo'q va xatolarga moyil emas. Masalan, default ishini tasvirlashni unutish yoki dastlabki holatni o'rnatmaslik oson. Create Reducer xususiyati har bir faoliyat turini boshqarish uchun xususiyatlarni qidirish jadvallari sifatida belgilash orqali Reducer xususiyatlarini yaratishni osonlashtiradi. Bundan tashqari, reducerlar ichida "o'zgaruvchan" uslubda kod yozib, immutable yangilash mantig'ini sezilarli darajada soddalashtirishga imkon beradi.
Immer kutubxonasidan foydalanish tufayli voqealarni qayta ishlash "immutable" uslubi mavjud. Funktsiya ishlov beruvchi xususiyatlarni o'zgartirish uchun uzatilgan staetni "mutatsiya" yoki immutable uslubda bo'lgani kabi yangi stateni qaytarishi mumkin, ammo Immer tufayli ob'ektning haqiqiy mutatsiyasi amalga oshirilmaydi. Birinchi variant, ayniqsa, chuqur joylashtirilgan ob'ektni o'zgartirganda, ishlash va idrok etish uchun ancha oson.
Ehtiyot bo'ling: yangi ob'ektni funktsiyadan qaytarish o'zgarishlarni qoplaydi. Vaziyatni yangilashning ikkala usulini bir vaqtning o'zida qo'llash ishlamaydi.
Kirish parametrlari sifatida create Reducer funksiyasi quyidagi argumentlarni qabul qiladi:
- saqlashning dastlabki holati,
- faoliyat turlari va redüktörler o'rtasida moslikni o'rnatadigan ob'ekt, ularning har biri ma'lum bir turni qayta ishlaydi.
CreateReducer usulidan foydalanib, biz quyidagi kodni olamiz:
const initialState = {
productReleases: [],
loadedProductRelease: null,
fetchingState: 'none',
creatingState: 'none',
loadingState: 'none',
error: null,
};
const counterReducer = createReducer(initialState, {
[productReleasesFetching]: (state, action) => {
state.fetchingState = 'requesting'
},
[productReleasesFetched.type]: (state, action) => {
state.productReleases = action.payload.productReleases;
state.fetchingState = 'success';
},
[productReleasesFetchingError]: (state, action) => {
state.fetchingState = 'failed';
state.error = action.payload.error;
},
…
[productReleaseUpdating]: (state) => {
state.updatingState = 'requesting'
},
[productReleaseUpdated]: (state, action) => {
state.updatingState = 'success';
state.productReleases = state.productReleases.map(productRelease => {
if (productRelease.id === action.payload.productRelease.id)
return action.payload.productRelease;
return productRelease;
});
},
[productReleaseUpdatingError]: (state, action) => {
state.updating = 'failed';
state.error = action.payload.error;
},
});
Ko'rib turganimizdek, createAction va createReducer funktsiyalaridan foydalanish ortiqcha kod yozish muammosini sezilarli darajada hal qiladi, ammo konstantalarni oldindan yaratish muammosi hali ham saqlanib qolmoqda. Shuning uchun, avlodni va harakat yaratuvchilarni va redücerni birlashtirgan yanada kuchli variantni ko'rib chiqing — createslice funktsiyasi.
Fuktsiya createSlice
Kirish parametrlari sifatida Create Slice funksiyasi quyidagi maydonlar bilan obyektni qabul qiladi:
- name — yaratilgan harakatlar nomlari maydoni (
${name}/${action.type}
); - initialState — radyuserning dastlabki holati;
- reducers — ishlov beruvchilar bilan ob'ekt. Har bir ishlov beruvchi state va action argumentlari bilan funktsiyani qabul qiladi, harakat payload xususiyatidagi ma'lumotlarni va ism xususiyatidagi voqea nomini o'z ichiga oladi. Bundan tashqari, hodisadan olingan ma'lumotlarni reduserga kirishdan oldin oldindan o'zgartirish mumkin (masalan, to'plam elementlariga id qo'shish). Buning uchun funktsiya o'rniga ob'ektni reducer va prepare maydonlari bilan o'tkazish kerak, bu erda reducer-bu harakatni qayta ishlash funktsiyasi va prepare-yangilangan payload-ni qaytaradigan foydali yuklarni qayta ishlash funktsiyasi;
- extraReducers — boshqa kesilgan redücerlarni o'z ichiga olgan ob'ekt. Agar boshqa bo'lim bilan bog'liq ob'ektni yangilash zarur bo'lsa, ushbu parametr talab qilinishi mumkin. Ushbu funktsional imkoniyat haqida ko'proq ma'lumotni rasmiy hujjatlarning tegishli bo'limidan olishingiz mumkin.
Funktsiyaning natijasi quyidagi maydonlarga ega bo'lgan "kesish" deb nomlangan ob'ektdir:
- name — kesish nomi,
- reducer — reduser,
- actions — harakatlar to'plami.
Muammoni hal qilish uchun ushbu funktsiyadan foydalanib, biz quyidagi manba kodini olamiz:
const initialState = {
productReleases: [],
loadedProductRelease: null,
fetchingState: 'none',
creatingState: 'none',
loadingState: 'none',
error: null,
};
const productReleases = createSlice({
name: 'productReleases',
initialState,
reducers: {
productReleasesFetching: (state) => {
state.fetchingState = 'requesting';
},
productReleasesFetched: (state, action) => {
state.productReleases = action.payload.productReleases;
state.fetchingState = 'success';
},
productReleasesFetchingError: (state, action) => {
state.fetchingState = 'failed';
state.error = action.payload.error;
},
…
productReleaseUpdating: (state) => {
state.updatingState = 'requesting'
},
productReleaseUpdated: (state, action) => {
state.updatingState = 'success';
state.productReleases = state.productReleases.map(productRelease => {
if (productRelease.id === action.payload.productRelease.id)
return action.payload.productRelease;
return productRelease;
});
},
productReleaseUpdatingError: (state, action) => {
state.updating = 'failed';
state.error = action.payload.error;
},
},
});
Endi biz yaratilgan bo'limdan harakatlar yaratuvchilari va redücerni chiqaramiz.
const { actions, reducer } = productReleases;
export const {
productReleasesFetched, productReleasesFetching,
productReleasesFetchingError,
…
productReleaseUpdated,
productReleaseUpdating, productReleaseUpdatingError
} = actions;
export default reducer;
API o'z ichiga olgan harakatlar yaratuvchilarining manba kodi o'zgarmadi, faqat harakatlar yuborilganda parametrlarni uzatish usuli bundan mustasno:
export const fetchProductReleases = () => (dispatch) => {
dispatch(productReleasesFetching());
return productReleasesService
.getProductReleases()
.then((productReleases) => dispatch(productReleasesFetched({ productReleases })))
.catch((error) => {
error.clientMessage = "Can't get product releases";
dispatch(productReleasesFetchingError({ error }));
});
};
…
export const updateProductRelease = (id, productName, productVersion, releaseDate) => (dispatch) => {
dispatch(productReleaseUpdating());
return productReleasesService
.updateProductRelease(id, productName, productVersion, releaseDate)
.then((productRelease) => dispatch(productReleaseUpdated({ productRelease })))
.catch((error) => {
error.clientMessage = "Can't update product releases";
dispatch(productReleaseUpdatingError({ error }));
});
Yuqoridagi kod shuni ko'rsatadiki, createSlice funktsiyasi Redux bilan ishlashda shablon kodida katta qismidan xalos bo'lishga imkon beradi, bu nafaqat kodni yanada ixcham, ixcham va tushunarli qilish, balki uni yozishga kamroq vaqt sarflash imkonini beradi.
Xulosa
Ushbu maqolaning oxirida shuni aytmoqchimanki, Redux Toolkit kutubxonasi state boshqarish uchun yangi hech narsa qo'shmasa ham, kod yozish uchun bir qator qulay vositalarni taqdim etadi.oldingisiga qaraganda. Ushbu vositalar kutubxonada ilgari yaxshi tashkil etilgan bir qator vositalar mavjudligi sababli nafaqat ishlab chiqish jarayonini yanada qulay, tushunarli va tezkor, balki samaraliroq qilish imkonini beradi. Biz, chet elda, dasturiy mahsulotlarimizni ishlab chiqishda ushbu kutubxonadan foydalanishni davom ettirishni va veb-texnologiyalar sohasidagi yangi istiqbolli ishlanmalarni kuzatishni rejalashtirmoqdamiz.