Redux — Обзор
Redux — это контейнер с предсказуемым состоянием для приложений JavaScript. По мере роста приложения становится трудно поддерживать его организованность и поддерживать поток данных. Redux решает эту проблему, управляя состоянием приложения с помощью одного глобального объекта с именем Store. Основные принципы Redux помогают поддерживать согласованность всего приложения, что упрощает отладку и тестирование.
Что еще более важно, он дает вам редактирование кода в реальном времени в сочетании с отладчиком путешествий во времени. Он может работать с любым слоем вида, таким как React, Angular, Vue и т. Д.
Принципы Redux
Предсказуемость Redux определяется тремя наиболее важными принципами, приведенными ниже:
Единый Источник Истины
Состояние всего вашего приложения хранится в дереве объектов в одном хранилище. Поскольку все состояние приложения хранится в одном дереве, это облегчает отладку и ускоряет разработку.
Состояние только для чтения
Единственный способ изменить состояние — это создать действие, объект, описывающий произошедшее. Это означает, что никто не может напрямую изменить состояние вашего приложения.
Изменения сделаны с чистыми функциями
Чтобы указать, как дерево состояний трансформируется действиями, вы пишете чистые редукторы. Редуктор — это центральное место, где происходит изменение состояния. Reducer — это функция, которая принимает состояние и действие в качестве аргументов и возвращает недавно обновленное состояние.
Redux — Установка
Перед установкой Redux мы должны установить Nodejs и NPM . Ниже приведены инструкции, которые помогут вам установить его. Вы можете пропустить эти шаги, если на вашем устройстве уже установлены Nodejs и NPM.
-
Посетите https://nodejs.org/ и установите файл пакета.
-
Запустите установщик, следуйте инструкциям и примите лицензионное соглашение.
-
Перезагрузите устройство, чтобы запустить его.
-
Вы можете проверить успешность установки, открыв командную строку и набрав узел -v. Это покажет вам последнюю версию Node в вашей системе.
-
Чтобы проверить, успешно ли установлена npm, вы можете набрать npm –v, которая вернет вам последнюю версию npm.
Посетите https://nodejs.org/ и установите файл пакета.
Запустите установщик, следуйте инструкциям и примите лицензионное соглашение.
Перезагрузите устройство, чтобы запустить его.
Вы можете проверить успешность установки, открыв командную строку и набрав узел -v. Это покажет вам последнюю версию Node в вашей системе.
Чтобы проверить, успешно ли установлена npm, вы можете набрать npm –v, которая вернет вам последнюю версию npm.
Чтобы установить redux, вы можете выполнить следующие шаги —
Выполните следующую команду в командной строке, чтобы установить Redux.
npm install --save redux
Чтобы использовать Redux с реагирующим приложением, вам нужно установить дополнительную зависимость следующим образом:
npm install --save react-redux
Чтобы установить инструменты разработчика для Redux, вам необходимо установить следующее в качестве зависимости:
Запустите приведенную ниже команду в командной строке, чтобы установить Redux dev-tools.
npm install --save-dev redux-devtools
Если вы не хотите устанавливать Redux dev tools и интегрировать его в свой проект, вы можете установить Redux DevTools Extension для Chrome и Firefox.
Redux — Основные понятия
Предположим, что состояние нашего приложения описывается простым объектом с именем initialState, который выглядит следующим образом:
const initialState = { isLoading: false, items: [], hasError: false };
Каждый фрагмент кода в вашем приложении не может изменить это состояние. Чтобы изменить состояние, вам нужно отправить действие.
Что такое действие?
Действие — это простой объект, который описывает намерение вызвать изменение с помощью свойства type. Он должен иметь свойство типа, которое сообщает, какой тип действия выполняется. Команда для действий выглядит следующим образом —
return { type: 'ITEMS_REQUEST', //action type isLoading: true //payload information }
Действия и состояния объединяются функцией редуктор. Действие отправляется с намерением вызвать изменение. Это изменение выполняется редуктором. Редуктор — единственный способ изменить состояния в Redux, делая его более предсказуемым, централизованным и отлаживаемым. Функция редуктора, которая обрабатывает действие ITEMS_REQUEST, выглядит следующим образом:
const reducer = (state = initialState, action) => { //es6 arrow function switch (action.type) { case 'ITEMS_REQUEST': return Object.assign({}, state, { isLoading: action.isLoading }) default: return state; } }
Redux имеет одно хранилище, в котором хранится состояние приложения. Если вы хотите разделить ваш код на основе логики обработки данных, вы должны начать разбивать ваши редукторы вместо хранилищ в Redux.
Мы обсудим, как мы можем разделить редукторы и объединить их с запасами позже в этом уроке.
Компоненты Redux следующие:
Redux — поток данных
Redux следует за однонаправленным потоком данных. Это означает, что данные вашего приложения будут следовать в одностороннем связующем потоке данных. По мере того как приложение растет и усложняется, становится трудно воспроизводить проблемы и добавлять новые функции, если вы не контролируете состояние своего приложения.
Redux уменьшает сложность кода, обеспечивая ограничение на то, как и когда может происходить обновление состояния. Таким образом, управление обновленными состояниями легко. Мы уже знаем об ограничениях как о трех принципах Redux. Следующая диаграмма поможет вам лучше понять поток данных Redux —
-
Действие отправляется, когда пользователь взаимодействует с приложением.
-
Корневая функция-редуктор вызывается с текущим состоянием и отправленным действием. Корневой редуктор может разделить задачу между меньшими функциями редуктора, что в конечном итоге возвращает новое состояние.
-
Хранилище уведомляет представление, выполняя их функции обратного вызова.
-
Представление может получить обновленное состояние и повторно выполнить рендеринг.
Действие отправляется, когда пользователь взаимодействует с приложением.
Корневая функция-редуктор вызывается с текущим состоянием и отправленным действием. Корневой редуктор может разделить задачу между меньшими функциями редуктора, что в конечном итоге возвращает новое состояние.
Хранилище уведомляет представление, выполняя их функции обратного вызова.
Представление может получить обновленное состояние и повторно выполнить рендеринг.
Redux — Магазин
Хранилище — это неизменяемое дерево объектов в Redux. Хранилище — это контейнер состояния, в котором хранится состояние приложения. Redux может иметь только один магазин в вашем приложении. Всякий раз, когда магазин создается в Redux, вам нужно указать редуктор.
Давайте посмотрим, как мы можем создать магазин, используя метод createStore из Redux. Нужно импортировать пакет createStore из библиотеки Redux, которая поддерживает процесс создания магазина, как показано ниже —
import { createStore } from 'redux'; import reducer from './reducers/reducer' const store = createStore(reducer);
Функция createStore может иметь три аргумента. Ниже приводится синтаксис —
createStore(reducer, [preloadedState], [enhancer])
Редуктор — это функция, которая возвращает следующее состояние приложения. PreloadedState является необязательным аргументом и является начальным состоянием вашего приложения. Энхансер также является необязательным аргументом. Это поможет вам улучшить магазин сторонними возможностями.
У магазина есть три важных метода, как указано ниже:
GetState
Это поможет вам получить текущее состояние вашего магазина Redux.
Синтаксис для getState выглядит следующим образом:
store.getState()
отправка
Это позволяет вам отправлять действие для изменения состояния в вашем приложении.
Синтаксис для отправки следующий:
store.dispatch({type:'ITEMS_REQUEST'})
подписываться
Это поможет вам зарегистрировать обратный вызов, который будет вызывать хранилище Redux при отправке действия. Как только состояние Redux будет обновлено, представление будет автоматически перерисовано.
Синтаксис для отправки следующий:
store.subscribe(()=>{ console.log(store.getState());})
Обратите внимание, что функция подписки возвращает функцию для отмены подписки слушателя. Чтобы отписаться от слушателя, мы можем использовать следующий код —
const unsubscribe = store.subscribe(()=>{console.log(store.getState());}); unsubscribe();
Redux — Действия
Действия — единственный источник информации для магазина согласно официальной документации Redux. Он несет полезную информацию из вашего приложения для хранения.
Как обсуждалось ранее, действия представляют собой простой объект JavaScript, который должен иметь атрибут типа, указывающий тип выполняемого действия. Это говорит нам о том, что случилось. Типы должны быть определены как строковые константы в вашем приложении, как указано ниже —
const ITEMS_REQUEST = 'ITEMS_REQUEST';
Помимо этого атрибута типа, структура объекта действия полностью зависит от разработчика. Рекомендуется сохранять объект действия как можно более легким и передавать только необходимую информацию.
Чтобы вызвать какие-либо изменения в хранилище, вам нужно сначала отправить действие с помощью функции store.dispatch (). Объект действия выглядит следующим образом —
{ type: GET_ORDER_STATUS , payload: {orderId,userId } } { type: GET_WISHLIST_ITEMS, payload: userId }
Создатели действий
Создатели действий — это функции, которые инкапсулируют процесс создания объекта действия. Эти функции просто возвращают простой объект Js, который является действием. Это способствует написанию чистого кода и помогает достичь возможности повторного использования.
Позвольте нам узнать о создателе действия, который позволяет вам отправить действие, ITEMS_REQUEST, которое запрашивает данные списка элементов продукта с сервера. Между тем, состояние isLoading выполняется в редукторе в типе действия ‘ITEMS_REQUEST’, чтобы указать, что элементы загружаются, а данные все еще не получены с сервера.
Первоначально состояние isLoading было ложным в объекте initialState, предполагая, что ничего не загружается. Когда данные получены в браузере, состояние isLoading будет возвращено как ложное в типе действия ‘ITEMS_REQUEST_SUCCESS’ в соответствующем редукторе. Это состояние может использоваться в качестве опоры в компонентах реагирования для отображения загрузчика / сообщения на вашей странице, когда включен запрос данных. Действие создателя заключается в следующем —
const ITEMS_REQUEST = ‘ITEMS_REQUEST’ ; const ITEMS_REQUEST_SUCCESS = ‘ITEMS_REQUEST_SUCCESS’ ; export function itemsRequest(bool,startIndex,endIndex) { let payload = { isLoading: bool, startIndex, endIndex } return { type: ITEMS_REQUEST, payload } } export function itemsRequestSuccess(bool) { return { type: ITEMS_REQUEST_SUCCESS, isLoading: bool, } }
Чтобы вызвать диспетчерскую функцию, вам нужно передать действие в качестве аргумента диспетчерской функции.
dispatch(itemsRequest(true,1, 20)); dispatch(itemsRequestSuccess(false));
Вы можете отправить действие напрямую, используя store.dispatch (). Однако более вероятно, что вы обращаетесь к нему с помощью вспомогательного метода реагирующего Redux, называемого connect () . Вы также можете использовать метод bindActionCreators (), чтобы связать многих создателей действий с функцией отправки.
Redux — чистые функции
Функция — это процесс, который принимает входные данные, называемые аргументами, и производит некоторые выходные данные, известные как возвращаемое значение. Функция называется чистой, если она соблюдает следующие правила:
-
Функция возвращает тот же результат для тех же аргументов.
-
Его оценка не имеет побочных эффектов, т. Е. Не изменяет входные данные.
-
Нет мутации локальных и глобальных переменных.
-
Он не зависит от внешнего состояния, как глобальная переменная.
Функция возвращает тот же результат для тех же аргументов.
Его оценка не имеет побочных эффектов, т. Е. Не изменяет входные данные.
Нет мутации локальных и глобальных переменных.
Он не зависит от внешнего состояния, как глобальная переменная.
Давайте возьмем пример функции, которая дважды возвращает значение, переданное в качестве входа в функцию. В общем, это записывается как, f (x) => x * 2. Если функция вызывается со значением аргумента 2, то результатом будет 4, f (2) => 4.
Давайте напишем определение функции в JavaScript, как показано ниже —
const double = x => x*2; // es6 arrow function console.log(double(2)); // 4
Здесь double — это чистая функция.
Согласно трем принципам в Redux, изменения должны быть сделаны чистой функцией, то есть редуктором в Redux. Теперь возникает вопрос, почему редуктор должен быть чистой функцией.
Предположим, вы хотите отправить действие, тип которого «ADD_TO_CART_SUCCESS», чтобы добавить элемент в вашу корзину, нажав кнопку «Добавить в корзину».
Давайте предположим, что редуктор добавляет товар в вашу корзину, как указано ниже —
const initialState = { isAddedToCart: false; } const addToCartReducer = (state = initialState, action) => { //es6 arrow function switch (action.type) { case 'ADD_TO_CART_SUCCESS' : state.isAddedToCart = !state.isAddedToCart; //original object altered return state; default: return state; } } export default addToCartReducer ;
Предположим, isAddedToCart — это свойство объекта состояния, которое позволяет вам решить, когда отключить кнопку «добавить в корзину» для элемента, возвращая логическое значение «истина или ложь» . Это не позволяет пользователю добавлять один и тот же продукт несколько раз. Теперь, вместо того, чтобы возвращать новый объект, мы изменяем свойство isAddedToCart в состоянии, как описано выше. Теперь, если мы попытаемся добавить товар в корзину, ничего не произойдет. Кнопка Добавить в корзину не будет отключена.
Причина такого поведения заключается в следующем —
Redux сравнивает старые и новые объекты по расположению в памяти обоих объектов. Он ожидает новый объект от редуктора, если произошло какое-либо изменение. И он также ожидает вернуть старый объект, если не произойдет никаких изменений. В этом случае это то же самое. По этой причине Redux предполагает, что ничего не произошло.
Таким образом, необходимо, чтобы редуктор был чистой функцией в Redux. Вот способ написать это без мутации:
const initialState = { isAddedToCart: false; } const addToCartReducer = (state = initialState, action) => { //es6 arrow function switch (action.type) { case 'ADD_TO_CART_SUCCESS' : return { ...state, isAddedToCart: !state.isAddedToCart } default: return state; } } export default addToCartReducer;
Redux — Редукторы
Редукторы — это чистая функция в Redux. Чистые функции предсказуемы. Редукторы — единственный способ изменить состояния в Redux. Это единственное место, где вы можете написать логику и вычисления. Функция Reducer примет предыдущее состояние приложения и отправляемого действия, вычислит следующее состояние и вернет новый объект.
Следующие несколько вещей никогда не должны выполняться внутри редуктора:
- Мутация аргументов функций
- API-вызовы и логика маршрутизации
- Вызов не чистой функции, например, Math.random ()
Ниже приведен синтаксис редуктора —
(state,action) => newState
Давайте продолжим пример показа списка товаров на веб-странице, который обсуждается в модуле создателей действий. Давайте посмотрим ниже, как написать его редуктор.
const initialState = { isLoading: false, items: [] }; const reducer = (state = initialState, action) => { switch (action.type) { case 'ITEMS_REQUEST': return Object.assign({}, state, { isLoading: action.payload.isLoading }) case ‘ITEMS_REQUEST_SUCCESS': return Object.assign({}, state, { items: state.items.concat(action.items), isLoading: action.isLoading }) default: return state; } } export default reducer;
Во-первых, если вы не установили состояние в «initialState», Redux вызывает reducer с неопределенным состоянием. В этом примере кода функция JavaScript concat () используется в ‘ITEMS_REQUEST_SUCCESS’, которая не меняет существующий массив; вместо этого возвращает новый массив.
Таким образом, вы можете избежать мутации состояния. Никогда не пишите напрямую государству. В ‘ITEMS_REQUEST’ мы должны установить значение состояния из полученного действия.
Уже обсуждалось, что мы можем записать нашу логику в редуктор и разделить ее на основе логических данных. Давайте посмотрим, как мы можем разделить редукторы и объединить их вместе в качестве корневого редуктора при работе с большим приложением.
Предположим, мы хотим создать веб-страницу, где пользователь может получить доступ к статусу заказа продукта и просмотреть информацию в списке пожеланий. Мы можем разделить логику в разных файлах редукторов и заставить их работать независимо. Предположим, что действие GET_ORDER_STATUS отправлено для получения статуса заказа, соответствующего некоторому идентификатору заказа и идентификатору пользователя.
/reducer/orderStatusReducer.js import { GET_ORDER_STATUS } from ‘../constants/appConstant’; export default function (state = {} , action) { switch(action.type) { case GET_ORDER_STATUS: return { ...state, orderStatusData: action.payload.orderStatus }; default: return state; } }
Аналогично, предположим, что действие GET_WISHLIST_ITEMS отправляется, чтобы получить информацию о списке пожеланий пользователя, соответствующую пользователю.
/reducer/getWishlistDataReducer.js import { GET_WISHLIST_ITEMS } from ‘../constants/appConstant’; export default function (state = {}, action) { switch(action.type) { case GET_WISHLIST_ITEMS: return { ...state, wishlistData: action.payload.wishlistData }; default: return state; } }
Теперь мы можем объединить оба редуктора с помощью утилиты Redux combReducers. CombReducers генерирует функцию, которая возвращает объект, значения которого являются различными функциями редуктора. Вы можете импортировать все редукторы в файл редуктора индекса и объединить их вместе как объект с соответствующими именами.
/reducer/index.js import { combineReducers } from ‘redux’; import OrderStatusReducer from ‘./orderStatusReducer’; import GetWishlistDataReducer from ‘./getWishlistDataReducer’; const rootReducer = combineReducers ({ orderStatusReducer: OrderStatusReducer, getWishlistDataReducer: GetWishlistDataReducer }); export default rootReducer;
Теперь вы можете передать этот rootReducer методу createStore следующим образом:
const store = createStore(rootReducer);
Redux — Middleware
Redux сам по себе является синхронным, так как асинхронные операции, такие как сетевой запрос, работают с Redux? Здесь промежуточные программы пригодятся. Как обсуждалось ранее, редукторы — это место, где пишется вся логика выполнения. Редуктор не имеет никакого отношения к тому, кто его выполняет, сколько времени он занимает или регистрирует состояние приложения до и после отправки действия.
В этом случае функция промежуточного программного обеспечения Redux обеспечивает среду для взаимодействия с отправленными действиями до того, как они достигнут редуктора. Настраиваемые функции промежуточного программного обеспечения могут быть созданы путем написания функций высокого порядка (функция, которая возвращает другую функцию), которая охватывает некоторую логику. Несколько промежуточных программ могут быть объединены вместе для добавления новых функций, и каждое промежуточное программное обеспечение не требует знания того, что было до и после. Вы можете представить себе промежуточное ПО где-то между направленным действием и редуктором.
Обычно промежуточное программное обеспечение используется для решения асинхронных действий в вашем приложении. Redux предоставляет API под названием applyMiddleware, который позволяет нам использовать пользовательское промежуточное программное обеспечение, а также промежуточное программное обеспечение Redux, например, redux-thunk и redux-обещание. Это относится к промежуточному программному обеспечению для хранения. Синтаксис использования applyMiddleware API —
applyMiddleware(...middleware)
И это может применяться для хранения следующим образом —
import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import rootReducer from './reducers/index'; const store = createStore(rootReducer, applyMiddleware(thunk));
Промежуточное ПО позволит вам написать диспетчер действий, который возвращает функцию вместо объекта действия. Пример для того же показан ниже —
function getUser() { return function() { return axios.get('/get_user_details'); }; }
Условная рассылка может быть написана внутри промежуточного программного обеспечения. Каждое промежуточное ПО получает диспетчеризацию хранилища, чтобы они могли отправлять новое действие, и функции getState в качестве аргументов, чтобы они могли обращаться к текущему состоянию и возвращать функцию. Любое возвращаемое значение из внутренней функции будет доступно как значение самой функции отправки.
Ниже приведен синтаксис промежуточного программного обеспечения —
({ getState, dispatch }) => next => action
Функция getState полезна для определения необходимости извлечения новых данных или возврата результатов кэширования в зависимости от текущего состояния.
Давайте посмотрим на пример пользовательской функции регистрации промежуточного программного обеспечения. Он просто регистрирует действие и новое состояние.
import { createStore, applyMiddleware } from 'redux' import userLogin from './reducers' function logger({ getState }) { return next => action => { console.log(‘action’, action); const returnVal = next(action); console.log('state when action is dispatched', getState()); return returnVal; } }
Теперь примените промежуточное программное обеспечение регистратора к магазину, написав следующую строку кода:
const store = createStore(userLogin , initialState=[ ] , applyMiddleware(logger));
Отправьте действие, чтобы проверить отправленное действие и новое состояние, используя следующий код —
store.dispatch({ type: 'ITEMS_REQUEST', isLoading: true })
Другой пример промежуточного программного обеспечения, где вы можете определить, когда показывать или скрывать загрузчик, приведен ниже. Это промежуточное ПО показывает загрузчик, когда вы запрашиваете какой-либо ресурс, и скрывает его, когда запрос ресурса завершен.
import isPromise from 'is-promise'; function loaderHandler({ dispatch }) { return next => action => { if (isPromise(action)) { dispatch({ type: 'SHOW_LOADER' }); action .then(() => dispatch({ type: 'HIDE_LOADER' })) .catch(() => dispatch({ type: 'HIDE_LOADER' })); } return next(action); }; } const store = createStore( userLogin , initialState = [ ] , applyMiddleware(loaderHandler) );
Redux — Devtools
Redux-Devtools предоставляет нам платформу для отладки приложений Redux. Это позволяет нам выполнять отладку во времени и редактирование в реальном времени. Некоторые из функций в официальной документации следующие:
-
Это позволяет вам проверять каждое состояние и полезную нагрузку.
-
Это позволяет вам вернуться в прошлое, «отменив» действия.
-
Если вы измените код редуктора, каждое «поэтапное» действие будет пересмотрено.
-
Если редукторы выбрасывают, мы можем определить ошибку, а также во время какого действия это произошло.
-
С помощью средства улучшения хранилища persistState () вы можете сохранять сеансы отладки при перезагрузке страницы.
Это позволяет вам проверять каждое состояние и полезную нагрузку.
Это позволяет вам вернуться в прошлое, «отменив» действия.
Если вы измените код редуктора, каждое «поэтапное» действие будет пересмотрено.
Если редукторы выбрасывают, мы можем определить ошибку, а также во время какого действия это произошло.
С помощью средства улучшения хранилища persistState () вы можете сохранять сеансы отладки при перезагрузке страницы.
Существует два варианта Redux dev-tools, как показано ниже —
Redux DevTools — он может быть установлен как пакет и интегрирован в ваше приложение, как указано ниже —
https://github.com/reduxjs/redux-devtools/blob/master/docs/Walkthrough.md#manual-integration
Redux DevTools Extension — Расширение браузера, которое реализует те же инструменты разработчика для Redux, выглядит следующим образом —
https://github.com/zalmoxisus/redux-devtools-extension
Теперь давайте проверим, как мы можем пропустить действия и вернуться в прошлое с помощью Redux dev tool. На следующих скриншотах рассказывается о действиях, которые мы отправили ранее для получения списка товаров. Здесь мы можем увидеть действия, отправленные на вкладке инспектора. Справа вы можете увидеть вкладку «Демо», которая показывает разницу в дереве состояний.
Вы познакомитесь с этим инструментом, когда начнете его использовать. Вы можете отправить действие, не записывая реальный код, только с помощью этого плагина Redux. Опция Dispatcher в последнем ряду поможет вам в этом. Давайте проверим последнее действие, когда элементы были успешно выбраны.
Мы получили массив объектов в ответ от сервера. Все данные доступны для отображения на нашей странице. Вы также можете одновременно отслеживать состояние магазина, нажав на вкладку состояния в правом верхнем углу.
В предыдущих разделах мы узнали об отладке путешествий во времени. Давайте теперь проверим, как пропустить одно действие и вернуться во времени, чтобы проанализировать состояние нашего приложения. При нажатии на любой тип действия появятся две опции: «Перейти» и «Пропустить».
Нажав на кнопку пропуска для определенного типа действия, вы можете пропустить конкретное действие. Это действует так, как будто действие никогда не происходило. Когда вы нажимаете кнопку перехода на определенном типе действия, он переводит вас в состояние, когда это действие произошло, и пропускает все оставшиеся действия в последовательности. Таким образом, вы сможете сохранить состояние, когда произошло конкретное действие. Эта функция полезна при отладке и поиске ошибок в приложении.
Мы пропустили последнее действие, и все данные листинга из фона исчезли. Это возвращает нас к тому времени, когда данные элементов еще не поступили, а наше приложение не имеет данных для отображения на странице. Это на самом деле облегчает кодирование и облегчает отладку.
Redux — Тестирование
Тестировать код Redux легко, так как мы в основном пишем функции, и большинство из них чистые. Таким образом, мы можем проверить это, даже не издеваясь над ними. Здесь мы используем JEST в качестве движка тестирования. Он работает в среде узла и не имеет доступа к DOM.
Мы можем установить JEST с кодом, приведенным ниже —
npm install --save-dev jest
С Babel вам необходимо установить Babel-Jest следующим образом:
npm install --save-dev babel-jest
И настройте его для использования функций babel-preset-env в файле .babelrc следующим образом:
{ "presets": ["@babel/preset-env"] } And add the following script in your package.json: { //Some other code "scripts": { //code "test": "jest", "test:watch": "npm test -- --watch" }, //code }
Наконец, запустите npm test или npm run test . Давайте проверим, как мы можем написать контрольные примеры для создателей действий и редукторов.
Тестовые случаи для создателей действий
Предположим, у вас есть создатель действий, как показано ниже —
export function itemsRequestSuccess(bool) { return { type: ITEMS_REQUEST_SUCCESS, isLoading: bool, } }
Этот создатель действий может быть протестирован, как указано ниже —
import * as action from '../actions/actions'; import * as types from '../../constants/ActionTypes'; describe('actions', () => { it('should create an action to check if item is loading', () => { const isLoading = true, const expectedAction = { type: types.ITEMS_REQUEST_SUCCESS, isLoading } expect(actions.itemsRequestSuccess(isLoading)).toEqual(expectedAction) }) })
Контрольные примеры для редукторов
Мы узнали, что редуктор должен возвращать новое состояние при применении действия. Так что редуктор проверен на это поведение.
Рассмотрим редуктор, как указано ниже —
const initialState = { isLoading: false }; const reducer = (state = initialState, action) => { switch (action.type) { case 'ITEMS_REQUEST': return Object.assign({}, state, { isLoading: action.payload.isLoading }) default: return state; } } export default reducer;
Чтобы протестировать вышеуказанный редуктор, нам нужно передать состояние и действие редуктору и вернуть новое состояние, как показано ниже:
import reducer from '../../reducer/reducer' import * as types from '../../constants/ActionTypes' describe('reducer initial state', () => { it('should return the initial state', () => { expect(reducer(undefined, {})).toEqual([ { isLoading: false, } ]) }) it('should handle ITEMS_REQUEST', () => { expect( reducer( { isLoading: false, }, { type: types.ITEMS_REQUEST, payload: { isLoading: true } } ) ).toEqual({ isLoading: true }) }) })
Если вы не знакомы с написанием тестового примера, вы можете проверить основы JEST .
Redux — интегрировать React
В предыдущих главах мы узнали, что такое Redux и как он работает. Давайте теперь проверим интеграцию части представления с Redux. Вы можете добавить любой слой представления в Redux. Мы также обсудим реакцию библиотеки и Redux.
Допустим, если различным реагирующим компонентам необходимо отображать одни и те же данные по-разному, не передавая их в качестве опоры всем компонентам от компонента верхнего уровня до пути вниз. Было бы идеально хранить его вне компонентов реакции. Потому что это помогает в более быстром извлечении данных, так как вам не нужно передавать данные полностью в разные компоненты.
Давайте обсудим, как это возможно с Redux. Redux предоставляет пакет Reaction-Redux для связывания реагирующих компонентов с двумя утилитами, как указано ниже:
- поставщик
- соединять
Провайдер делает магазин доступным для остальных приложений. Функция подключения помогает реагировать компоненту на подключение к магазину, реагируя на каждое изменение, происходящее в состоянии магазина.
Давайте посмотрим на корневой файл index.js, который создает хранилище и использует провайдера, который разрешает хранение для остальной части приложения в приложенииact-redux.
import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' import { createStore, applyMiddleware } from 'redux'; import reducer from './reducers/reducer' import thunk from 'redux-thunk'; import App from './components/app' import './index.css'; const store = createStore( reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), applyMiddleware(thunk) ) render( <Provider store = {store}> <App /> </Provider>, document.getElementById('root') )
Всякий раз, когда происходит изменение в приложенииact-redux, вызывается mapStateToProps (). В этой функции мы точно указываем, какое состояние нам нужно предоставить нашему компоненту реакции.
С помощью функции connect (), описанной ниже, мы соединяем состояние этих приложений с компонентом реакции. Connect () — это функция высокого порядка, которая принимает компонент в качестве параметра. Он выполняет определенные операции и возвращает новый компонент с правильными данными, которые мы наконец экспортировали.
С помощью mapStateToProps () мы предоставляем эти состояния хранилища в качестве опоры для нашего реагирующего компонента. Этот код может быть заключен в компонент контейнера. Мотив состоит в том, чтобы разделить проблемы, такие как выборка данных, рендеринг и повторное использование.
import { connect } from 'react-redux' import Listing from '../components/listing/Listing' //react component import makeApiCall from '../services/services' //component to make api call const mapStateToProps = (state) => { return { items: state.items, isLoading: state.isLoading }; }; const mapDispatchToProps = (dispatch) => { return { fetchData: () => dispatch(makeApiCall()) }; }; export default connect(mapStateToProps, mapDispatchToProps)(Listing);
Определение компонента для выполнения вызова API в файле services.js выглядит следующим образом:
import axios from 'axios' import { itemsLoading, itemsFetchDataSuccess } from '../actions/actions' export default function makeApiCall() { return (dispatch) => { dispatch(itemsLoading(true)); axios.get('http://api.tvmaze.com/shows') .then((response) => { if (response.status !== 200) { throw Error(response.statusText); } dispatch(itemsLoading(false)); return response; }) .then((response) => dispatch(itemsFetchDataSuccess(response.data))) }; }
Функция mapDispatchToProps () получает функцию отправки в качестве параметра и возвращает реквизиты обратного вызова в виде простого объекта, который вы передаете компоненту реагирования.
Здесь вы можете получить доступ к fetchData в качестве опоры в вашем компоненте списка реагирования, который отправляет действие для вызова API. mapDispatchToProps () используется для отправки действия для сохранения. В реакции-избыточности компоненты не могут получить прямой доступ к хранилищу. Единственный способ — использовать connect ().
Давайте разберемся, как реагирует на редукс через схему ниже —
STORE — сохраняет все состояние вашего приложения как объект JavaScript
PROVIDER — делает магазины доступными
КОНТЕЙНЕР — Получить состояние приложений и предоставить его в качестве опоры для компонентов
КОМПОНЕНТ — Пользователь взаимодействует через компонент просмотра
ДЕЙСТВИЯ — Вызывает изменение в магазине, это может или не может изменить состояние вашего приложения
REDUCER — единственный способ изменить состояние приложения, принять состояние и действие и вернуть обновленное состояние.
Тем не менее, Redux является независимой библиотекой и может использоваться с любым уровнем пользовательского интерфейса. React-redux является официальным Redux, пользовательским интерфейсом, связывающим с реагировать. Более того, это способствует хорошей реакции на структуру приложения Redux. React-redux внутренне реализует оптимизацию производительности, поэтому повторный рендеринг компонентов происходит только тогда, когда это необходимо.
Подводя итог, Redux не предназначен для написания самого короткого и самого быстрого кода. Он предназначен для обеспечения предсказуемого контейнера управления состоянием. Это помогает нам понять, когда изменилось определенное состояние или откуда пришли данные.
Redux — пример реакции
Вот небольшой пример реакции и приложения Redux. Вы также можете попробовать разработать небольшие приложения. Пример кода для увеличения или уменьшения счетчика приведен ниже —
Это корневой файл, который отвечает за создание хранилища и рендеринг компонента нашего реагирующего приложения.
/src/index.js import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' import { createStore } from 'redux'; import reducer from '../src/reducer/index' import App from '../src/App' import './index.css'; const store = createStore( reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ) render( <Provider store = {store}> <App /> </Provider>, document.getElementById('root') )
Это наша корневая составляющая реакции. Он отвечает за рендеринг компонента контейнера счетчика как дочернего.
/src/app.js import React, { Component } from 'react'; import './App.css'; import Counter from '../src/container/appContainer'; class App extends Component { render() { return ( <div className = "App"> <header className = "App-header"> <Counter/> </header> </div> ); } } export default App;
Ниже приведен компонент контейнера, который отвечает за предоставление состояния Redux для реагирования компонента:
/container/counterContainer.js import { connect } from 'react-redux' import Counter from '../component/counter' import { increment, decrement, reset } from '../actions'; const mapStateToProps = (state) => { return { counter: state }; }; const mapDispatchToProps = (dispatch) => { return { increment: () => dispatch(increment()), decrement: () => dispatch(decrement()), reset: () => dispatch(reset()) }; }; export default connect(mapStateToProps, mapDispatchToProps)(Counter);
Ниже приведен компонент реакции, отвечающий за просмотр части —
/component/counter.js import React, { Component } from 'react'; class Counter extends Component { render() { const {counter,increment,decrement,reset} = this.props; return ( <div className = "App"> <div>{counter}</div> <div> <button onClick = {increment}>INCREMENT BY 1</button> </div> <div> <button onClick = {decrement}>DECREMENT BY 1</button> </div> <button onClick = {reset}>RESET</button> </div> ); } } export default Counter;
Ниже перечислены создатели действий, отвечающие за создание действий.
/actions/index.js export function increment() { return { type: 'INCREMENT' } } export function decrement() { return { type: 'DECREMENT' } } export function reset() { return { type: 'RESET' } }
Ниже мы показали строку кода для файла редуктора, который отвечает за обновление состояния в Redux.
reducer/index.js const reducer = (state = 0, action) => { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 case 'RESET' : return 0 default: return state } } export default reducer;
Изначально приложение выглядит следующим образом —
Когда я нажимаю инкремент два раза, экран вывода будет таким, как показано ниже —
Когда мы уменьшаем его один раз, он показывает следующий экран —
Сброс вернет приложение в исходное состояние, равное значению счетчика 0. Это показано ниже —
Давайте разберемся, что происходит с инструментами Redux dev, когда происходит первое действие приращения —
Состояние приложения будет перенесено во время, когда отправляется только действие приращения, а остальные действия пропускаются.
Мы рекомендуем самостоятельно разработать небольшое приложение Todo и лучше понять инструмент Redux.