portaldacalheta.pt
  • Основен
  • Инженерно Управление
  • Мобилен Дизайн
  • Разпределени Екипи
  • Пъргав
Back-End

Неизменяемост в JavaScript с помощта на Redux



Във все по-нарастваща екосистема от богати и сложни приложения на JavaScript има повече състояние за управление от всякога: текущият потребител, списъкът с заредени публикации и т.н.

Всеки набор от данни, който се нуждае от история на събитията, може да се счита за състояние. Управлението на състоянието може да бъде трудно и склонно към грешки, но работата с неизменяеми данни (а не с променливи) и някои поддържащи технологии - а именно Redux, за целите на тази статия - може да помогне значително.



Неизменяемите данни имат ограничения, а именно, че не могат да бъдат променяни, след като бъдат създадени, но също така имат много предимства, особено по отношение на равенството спрямо стойността, което може значително да ускори приложенията, които разчитат на често сравняване на данни (проверка дали нещо трябва да се актуализира , например).



Използването на неизменни състояния ни позволява да напишем код, който може бързо да разбере дали състоянието се е променило, без да е необходимо да правим рекурсивно сравнение на данните, което обикновено е много, много по-бързо.



Тази статия ще обхване практическите приложения на Redux при управление на състоянието чрез създатели на действия, чисти функции, съставени редуктори, нечисти действия с Redux-saga и Redux Thunk и накрая, използването на Redux с React. Въпреки това има много алтернативи на Redux, като библиотеки, базирани на MobX, Relay и Flux.

Защо Redux?

Ключовият аспект, който разделя Redux от повечето други държавни контейнери като MobX, Relay и повечето други изпълнения, базирани на Flux, е, че Redux има едно състояние, което може да бъде модифицирано само чрез „действия“ (обикновени JavaScript обекти), които се изпращат до Магазин Redux. Повечето други хранилища за данни имат състоянието, съдържащо се в самите компоненти на React, позволяват ви да имате множество хранилища и / или да използвате изменяемо състояние.



Това от своя страна кара редуктора на магазина, чиста функция, която работи върху неизменяеми данни, да изпълнява и потенциално актуализира състоянието. Този процес налага еднопосочен поток от данни, който е по-лесен за разбиране и по-детерминиран.

Потокът Redux.



Тъй като редукторите Redux са чисти функции, работещи върху неизменяеми данни, те винаги произвеждат един и същ изход при един и същи вход, което ги прави лесни за тестване. Ето пример за редуктор:

import Immutable from 'seamless-immutable' const initialState = Immutable([]) // create immutable array via seamless-immutable /** * a reducer takes a state (the current state) and an action object (a plain JavaScript object that was dispatched via dispatch(..) and potentially returns a new state. */ function addUserReducer(state = initialState, action) { if (action.type === 'USERS_ADD') { return state.concat(action.payload) } return state // note that a reducer MUST return a value } // somewhere else... store.dispatch({ type: 'USERS_ADD', payload: user }) // dispatch an action that causes the reducer to execute and add the user

Справянето с чисти функции позволява на Redux лесно да поддържа много случаи на употреба, които обикновено не се правят лесно с мутативно състояние, като например:



  • Пътуване във времето (връщане назад във времето към предишно състояние)
  • Регистрация (Проследявайте всяко едно действие, за да разберете какво е причинило мутация в магазина)
  • Среди за сътрудничество (като GoogleDocs, където действията са обикновени обекти на JavaScript и могат да бъдат сериализирани, изпратени по кабела и повторно възпроизведени на друга машина)
  • Лесно докладване на грешки (Просто изпратете списъка с изпратени действия и ги повторете, за да получите точно същото състояние)
  • Оптимизирано изобразяване (Най-малко в рамки, които визуализират виртуалния DOM като функция на състоянието, като React: поради неизменността можете лесно да разберете дали нещо се е променило чрез сравняване на препратки, за разлика от рекурсивното сравняване на обектите)
  • Тествайте лесно вашите редуктори, тъй като чистите функции могат лесно да бъдат тествани единично

Създатели на действия

Създателите на действия на Redux помагат за поддържането на кода чист и проверяем. Не забравяйте, че „действията“ в Redux не са нищо повече от обикновени JavaScript обекти, описващи мутация, която трябва да възникне. Като се има предвид това, изписването на едни и същи обекти отново и отново е повтарящо се и склонно към грешки.

най-добри практики за дизайн на интерфейса

Създателят на действия в Redux е просто помощна функция, която връща обикновен JavaScript обект, описващ мутация. Това помага за намаляване на повтарящия се код и поддържа всичките ви действия на едно място:



export function usersFetched(users) { return { type: 'USERS_FETCHED', payload: users, } } export function usersFetchFailed(err) { return { type: 'USERS_FETCH_FAILED', payload: err, } } // reducer somewhere else... const initialState = Immutable([]) // create immutable array via seamless-immutable /** * a reducer takes a state (the current state) and an action object (a plain JavaScript object that was dispatched via dispatch(..) and potentially returns a new state. */ function usersFetchedReducer(state = initialState, action) { if (action.type === 'USERS_FETCHED') { return Immutable(action.payload) } return state // note that a reducer MUST return a value }

Използване на Redux с неизменяеми библиотеки

Въпреки че самата природа на редукторите и действията ги правят лесни за тестване, без помощна библиотека за неизменност, няма нищо, което да ви предпазва от мутиращи обекти, което означава, че тестовете за всичките ви редуктори трябва да бъдат особено стабилни.

Помислете за следния пример на код за проблем, който ще срещнете без библиотека, за да ви защити:



const initialState = [] function addUserReducer(state = initialState, action) { if (action.type === 'USERS_ADD') { state.push(action.payload) // NOTE: mutating action!! return state } return state // note that a reducer MUST return a value }

В този пример с код пътуването във времето ще бъде нарушено, тъй като предишното състояние сега ще бъде същото като текущото състояние, чистите компоненти може да не се актуализират (или да се изобразят повторно), тъй като препратката към състоянието не се е променила, въпреки че данните съдържа се промени и мутациите са много по-трудни за разсъждаване.

Без библиотека за неизменност губим всички предимства, които Redux предоставя. Затова е силно препоръчително да използвате помощна библиотека за неизменяемост, като imutable.js или безпроблемно неизменяема, особено когато работите в голям екип с множество ръце, докосващи код.

Независимо коя библиотека използвате, Redux ще се държи по същия начин. Нека сравним плюсовете и минусите и на двете, за да можете да изберете кой от тях е най-подходящ за вашия случай на употреба:

Immutable.js

Immutable.js е библиотека, създадена от Facebook, с по-функционален стил на структурите на данни, като Карти, Списъци, Набори и Поредици. Нейната библиотека от неизменяеми постоянни структури от данни извършва възможно най-малкото копиране между различните състояния.

Професионалисти:

  • Структурно споделяне
  • По-ефективни при актуализации
  • По-ефективна памет
  • Има набор от помощни методи за управление на актуализации

Минуси:

  • Не работи безпроблемно със съществуващите JS библиотеки (т.е. lodash, ramda)
  • Изисква преобразуване към и от (към JS / от JS), особено по време на хидратация / дехидратация и визуализация

Безпроблемно неизменяем

Безпроблемно неизменяем е библиотека за неизменяеми данни, която е обратно съвместима чак до ES5.

Тя се основава на функции за дефиниране на свойствата на ES5, като defineProperty(..) за да деактивирате мутации на обекти. Като такъв той е напълно съвместим със съществуващите библиотеки като lodash и Ramda. Той може да бъде деактивиран и в производствените компилации, осигурявайки потенциално значително увеличение на производителността.

Професионалисти:

научете C++ код
  • Работи безпроблемно със съществуващите JS библиотеки (т.е. lodash, ramda)
  • Не е необходим допълнителен код за поддържане на преобразуване
  • Проверките могат да бъдат деактивирани в производствените компилации, увеличавайки производителността

Минуси:

  • Няма структурно споделяне - обектите / масивите се плитко копират, което го прави по-бавен за големи набори от данни
  • Не е толкова ефективно за паметта

Redux и множество редуктори

Друга полезна характеристика на Redux е способността да съставяте редуктори заедно, Това ви позволява да създавате много по-сложни приложения и в приложение с какъвто и да е осезаем размер неизбежно ще имате няколко типа състояния (текущ потребител, списъкът с заредени публикации, и т.н.). Redux поддържа (и насърчава) този случай на употреба, като предоставя естествено функцията combineReducers:

import { combineReducers } from 'redux' import currentUserReducer from './currentUserReducer' import postsListReducer from './postsListReducer' export default combineReducers({ currentUser: currentUserReducer, postsList: postsListReducer, })

С горния код можете да имате компонент, който разчита на currentUser и друг компонент, който разчита на postsList. Това също подобрява производителността, тъй като всеки отделен компонент ще се абонира само за всички клонове на дървото, които ги касаят.

Нечисти действия в Redux

По подразбиране можете да изпращате само обикновени обекти на JavaScript в Redux. С междинния софтуер обаче Redux може да поддържа нечисти действия като получаване на текущото време, изпълнение на мрежова заявка, запис на файл на диск и т.н.

„Middleware“ е терминът, използван за функции, които могат да прихващат изпращаните действия. Веднъж прихванат, той може да прави неща като трансформиране на действието или изпращане на асинхронно действие, подобно на междинния софтуер в други рамки (като Express.js).

Две много често срещани библиотеки за мидълуер са Redux Thunk и Redux-saga. Redux Thunk е написан в императивен стил, докато Redux-saga е написан във функционален стил. Нека сравним и двете.

Redux Thunk

Redux Thunk поддържа нечисти действия в Redux, като използва thunks, функции, които връщат други функции, които могат да се свързват с вериги. За да използвате Redux-Thunk, първо трябва да монтирате междинния софтуер Redux Thunk в магазина:

import { createStore, applyMiddleware } from 'redux' import thunk from 'redux-thunk' const store = createStore( myRootReducer, applyMiddleware(thunk), // here, we apply the thunk middleware to R )

Сега можем да изпълняваме нечисти действия (като например извършване на извикване на API), като изпращаме сигнал в магазина Redux:

store.dispatch( dispatch => { return api.fetchUsers() .then(users => dispatch(usersFetched(users)) // usersFetched is a function that returns a plain JavaScript object (Action) .catch(err => dispatch(usersFetchError(err)) // same with usersFetchError } )

Важно е да се отбележи, че използването на thunks може да направи кода ви труден за тестване и затруднява разсъжденията чрез потока на кода.

роли и отговорности на главен финансов директор

Редукс-сага

Redux-saga поддържа нечисти действия чрез ES6 (ES2015) функция, наречена генератори и библиотека от функционални / чисти помощници. Най-хубавото при генераторите е, че те могат да бъдат възобновени и поставени на пауза, а договорът им за API ги прави изключително лесни за тестване.

Нека да видим как можем да подобрим четливостта и проверимостта на предишния метод на thunk, използвайки саги!

Първо, нека монтираме междинния софтуер Redux-saga в нашия магазин:

import { createStore, applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' import rootReducer from './rootReducer' import rootSaga from './rootSaga' // create the saga middleware const sagaMiddleware = createSagaMiddleware() // mount the middleware to the store const store = createStore( rootReducer, applyMiddleware(sagaMiddleware), ) // run our saga! sagaMiddleware.run(rootSaga)

Имайте предвид, че run(..) функцията трябва да бъде извикана заедно със сагата, за да започне да се изпълнява.

Сега нека създадем нашата сага:

import { call, put, takeEvery } from 'redux-saga/effects' // these are saga effects we'll use export function *fetchUsers(action) { try { const users = yield call(api.fetchUsers) yield put(usersFetched(users)) } catch (err) { yield put(usersFetchFailed(err)) } } export default function *rootSaga() { yield takeEvery('USERS_FETCH', fetchUsers) }

Дефинирахме две функции на генератор, едната, която извлича списъка с потребители и rootSaga. Забележете, че не се обадихме api.fetchUsers директно, но вместо това го даде в обект на повикване. Това е така, защото Redux-saga прихваща обекта на повикване и изпълнява функцията, съдържаща се в него, за да създаде чиста среда (що се отнася до вашите генератори).

rootSaga дава едно извикване на функция, наречена takeEvery, който предприема всяко действие, изпратено с тип USERS_FETCH и извиква fetchUsers сага с действието, което предприе. Както виждаме, това създава много предсказуем модел на странични ефекти за Redux, който улеснява тестването!

Тестване на саги

Нека да видим как генераторите правят нашите саги лесни за тестване. Ще използваме мока в тази част да стартираме нашите модулни тестове и чай за твърдения.

Тъй като сагите дават обикновени JavaScript обекти и се изпълняват в генератор, можем лесно да тестваме дали те изпълняват правилното поведение без никакви подигравки! Имайте предвид, че call , take , put и т.н. са просто обикновени обекти на JavaScript, които се прихващат от междинния софтуер Redux-saga.

import { take, call } from 'redux-saga/effects' import { expect } from 'chai' import { rootSaga, fetchUsers } from '../rootSaga' describe('saga unit test', () => { it('should take every USERS_FETCH action', () => { const gen = rootSaga() // create our generator iterable expect(gen.next().value).to.be.eql(take('USERS_FETCH')) // assert the yield block does have the expected value expect(gen.next().done).to.be.equal(false) // assert that the generator loops infinitely }) it('should fetch the users if successful', () => { const gen = fetchUsers() expect(gen.next().value).to.be.eql(call(api.fetchUsers)) // expect that the call effect was yielded const users = [ user1, user2 ] // some mock response expect(gen.next(users).value).to.be.eql(put(usersFetched(users)) }) it('should fail if API fails', () => { const gen = fetchUsers() expect(gen.next().value).to.be.eql(call(api.fetchUsers)) // expect that the call effect was yielded const err = { message: 'authentication failed' } // some mock error expect(gen.throw(err).value).to.be.eql(put(usersFetchFailed(err)) }) })

Работа с React

Въпреки че Redux не е обвързан с конкретна спътникова библиотека, той работи особено добре с React.js тъй като React компонентите са чисти функции, които вземат състояние като вход и създават виртуален DOM като изход.

React-Redux е помощна библиотека за React и Redux, която елиминира повечето от усилената работа, свързваща двете. За да използваме най-ефективно React-Redux, нека разгледаме понятието за презентационни компоненти и компоненти на контейнера.

Презентационните компоненти описват как нещата трябва да изглеждат визуално, в зависимост единствено от техния реквизит, който да се изобрази; те извикват обратно извикване от реквизит до експедиционни действия. Те са написани на ръка, напълно чисти и не са обвързани със системи за държавно управление като Redux.

Компонентите на контейнера, от друга страна, описват как трябва да функционират нещата, знаят за Redux, изпращат Redux действия директно за извършване на мутации и обикновено се генерират от React-Redux. Те често се сдвояват с презентационен компонент, предоставящ неговия реквизит.

Презентационни компоненти и компоненти на контейнери в Redux.

Нека напишем презентационен компонент и да го свържем с Redux чрез React-Redux:

const HelloWorld = ({ count, onButtonClicked }) => ( Hello! You've clicked the button {count} times! Click me ) HelloWorld.propTypes = { count: PropTypes.number.isRequired, onButtonClicked: PropTypes.func.isRequired, }

Имайте предвид, че това е „тъп“ компонент, който напълно разчита на своите подпори, за да функционира. Това е страхотно, защото прави React компонент лесен за тестване и лесен за композиране . Нека да разгледаме как да свържем този компонент с Redux сега, но първо нека разгледаме какво е компонент от по-висок ред.

Компоненти от по-висок ред

React-Redux предоставя помощна функция, наречена connect( .. ) който създава компонент от по-висок ред от „тъп“ компонент на React, който знае за Redux.

шаблон на документ за технически дизайн на софтуер

React подчертава разширяемостта и повторната използваемост чрез състава, което е, когато увивате компоненти в други компоненти. Опаковането на тези компоненти може да промени поведението им или да добави нова функционалност. Нека да видим как можем да създадем компонент от по-висок ред от нашия презентационен компонент, който е запознат с Redux - компонент на контейнер.

Ето как го правите:

import { connect } from 'react-redux' const mapStateToProps = state => { // state is the state of our store // return the props that we want to use for our component return { count: state.count, } } const mapDispatchToProps = dispatch => { // dispatch is our store dispatch function // return the props that we want to use for our component return { onButtonClicked: () => { dispatch({ type: 'BUTTON_CLICKED' }) }, } } // create our enhancer function const enhancer = connect(mapStateToProps, mapDispatchToProps) // wrap our 'dumb' component with the enhancer const HelloWorldContainer = enhancer(HelloWorld) // and finally we export it export default HelloWorldContainer

Имайте предвид, че дефинирахме две функции, mapStateToProps и mapDispatchToProps .

mapStateToProps е чиста функция на (състояние: Обект), която връща обект, изчислен от състоянието Redux. Този обект ще бъде обединен с реквизита, предаден на опакования компонент. Това е известно и като селектор, тъй като избира части от състоянието Redux, които да бъдат обединени в подпорите на компонента.

mapDispatchToProps също е чиста функция, но една от (dispatch: (Action) => void), която връща обект, изчислен от функцията Redux dispatch. Този обект също ще бъде обединен с реквизита, предаден на обвития компонент.

Сега, за да използваме компонента на контейнера, трябва да използваме Provider компонент в React-Redux, за да каже на компонента на контейнера какъв магазин да използва:

import { Provider } from 'react-redux' import { render } from 'react-dom' import store from './store' // where ever your Redux store resides import HelloWorld from './HelloWorld' render( ( ), document.getElementById('container') )

Provider компонент разпространява магазина до всички дъщерни компоненти, които са абонирани за магазина Redux, запазвайки всичко на едно място и намалявайки точките на грешка или мутация!

Изградете доверие на кода с Redux

С това новооткрито познание за Redux, неговите многобройни поддържащи библиотеки и неговата рамкова връзка с React.js, можете лесно да ограничите броя на мутациите във вашето приложение чрез държавен контрол. Силният държавен контрол от своя страна ви позволява да се движите по-бързо и да създавате солидна кодова основа с повече увереност.

Стартирайте вашето PHP тестване с Codeception

Back-End

Стартирайте вашето PHP тестване с Codeception
Safe by Design - Преглед на UX сигурността

Safe by Design - Преглед на UX сигурността

Ux Дизайн

Популярни Публикации
Плащане напред: Разбиране на изкупувания с ливъридж
Плащане напред: Разбиране на изкупувания с ливъридж
Индустриален анализ и Porter’s Five Force: По-задълбочен поглед върху силата на купувача
Индустриален анализ и Porter’s Five Force: По-задълбочен поглед върху силата на купувача
Разширена реалност vs. Виртуална реалност vs. Смесена реалност: Уводно ръководство
Разширена реалност vs. Виртуална реалност vs. Смесена реалност: Уводно ръководство
Ще отвори ли Spotify не-IPO пътя за технологичните компании?
Ще отвори ли Spotify не-IPO пътя за технологичните компании?
Прогнозиране на харесвания: Вътре в алгоритмите на прост механизъм за препоръки
Прогнозиране на харесвания: Вътре в алгоритмите на прост механизъм за препоръки
 
Ефективни стартови платки: какви са те и как да ги изградим
Ефективни стартови платки: какви са те и как да ги изградим
Ръководител на клиентския опит
Ръководител на клиентския опит
Игла в купа сено: чудесен урок за мащабен текстов алгоритъм за търсене
Игла в купа сено: чудесен урок за мащабен текстов алгоритъм за търсене
Структурата на данните Trie: Пренебрегван скъпоценен камък
Структурата на данните Trie: Пренебрегван скъпоценен камък
Краят на уеб формите
Краят на уеб формите
Популярни Публикации
  • научете c/c++
  • колко струва индустрията за красота през 2017 г
  • как да разработим език за програмиране
  • как да разбием номер на кредитна карта
  • как да използвам c++
  • Apple Pay за потребители на Android
  • как да изчислим стойността на опцията
Категории
  • Инженерно Управление
  • Мобилен Дизайн
  • Разпределени Екипи
  • Пъргав
  • © 2022 | Всички Права Запазени

    portaldacalheta.pt