Mapdispatchtoprops redux что это
Перейти к содержимому

Mapdispatchtoprops redux что это

  • автор:

Connect: Отправка действий с помощью mapDispatchToProps

В качестве второго аргумента, передаваемого в connect , mapDispatchToProps используется для отправки(dispatch) действий в хранилище(store).

dispatch — это функция Redux хранилища(store). Вы вызываете store.dispatch , чтобы отправить действие. Это единственный способ вызвать изменение состояния.

С React Redux ваши компоненты никогда не обращаются к хранилищу(store) напрямую — connect делает это за вас. React Redux дает вам два способа разрешить компонентам отправлять действия:

  • По умолчанию подключенный компонент получает props.dispatch и может сам отправлять действия.
  • connect может принимать аргумент с именем mapDispatchToProps , который позволяет вам создавать функции, отправляющие действие при вызове, и передавать их в качестве пропсов вашему компоненту.

Функции mapDispatchToProps обычно для краткости называются mapDispatch , но фактическое используемое имя переменной может быть любым.

Подходы к отправке(dispatch)​

По умолчанию: dispatch как пропс​

Если вы не укажете второй аргумент для connect() , ваш компонент по умолчанию получит dispatch . Пример:

connect()(MyComponent) // эквивалентно connect(null, null)(MyComponent)  // или connect(mapStateToProps /** нет второго аргумента */)(MyComponent) 

После того, как вы подключили свой компонент таким образом, ваш компонент получает props.dispatch . Вы можете использовать его для отправки действий в хранилище(store).

function Counter( count, dispatch >)   return ( div> button onClick=() => dispatch( type: 'DECREMENT' >)>>-/button> span>count>/span> button onClick=() => dispatch( type: 'INCREMENT' >)>>+/button> button onClick=() => dispatch( type: 'RESET' >)>>reset/button> /div> ) > 

Используем параметр mapDispatchToProps ​

Использование mapDispatchToProps позволяет вам указать, какие действия сможет отправить ваш компонент. Он позволяет вам предоставлять функции отправки действий в качестве пропсов. Поэтому вместо вызова props.dispatch(() => increment()) вы можете напрямую вызвать props.increment() . Есть несколько причин, по которым вы можете захотеть это сделать.

Это более декларативный подход​

Во-первых, инкапсуляция логики отправки(dispatch) в функцию делает реализацию более декларативной. Отправка действия и предоставление возможности Redux хранилищу(store) обрабатывать поток данных — отвечает на вопрос как реализовать поведение, нежели что оно делает.

Хорошим примером может быть отправка(dispatch) действия при нажатии кнопки. Вызов функции отправки напрямую в кнопке не имеет смысла концептуально, поскольку в таком случае кнопка должна знать о функции dispatch .

// кнопка должна знать об "dispatch" button onClick=() => dispatch( type: "SOMETHING" >)> />  // кнопка не знает об "dispatch", button onClick=doSomething> /> 

После того, как вы обернули все наши создатели действий функциями, которые отправляют действия, компонент свободен от необходимости вызывать функцию dispatch . Следовательно, если вы определите свой собственный mapDispatchToProps , подключенный компонент больше не будет получать dispatch .

Передача логики отправки(dispatch) действия в ( неподключенные ) дочерние компоненты​

Кроме того, вы также получаете возможность передавать функции отправки действий дочерним (вероятно, неподключенным) компонентам. Это позволяет большему количеству компонентов отправлять действия, оставляя их «не осведомленными» о Redux.

// передача toggleTodo дочернему компоненту // предоставление Todo возможности отправлять(dispatch) действие toggleTodo const TodoList = ( todos, toggleTodo >) => ( div> todos.map((todo) => ( Todo todo=todo> onClick=toggleTodo> /> ))> div> ) 

Это то, что делает connect React Redux — он инкапсулирует логику общения с Redux хранилищем(store) и позволяет вам не беспокоиться о ней. И это то, что вы точно должны использовать в своей реализации.

Две формы mapDispatchToProps ​

Параметр mapDispatchToProps может иметь две формы. В то время как функциональная форма допускает больше настроек, объектная форма более проста в использовании.

  • Функциональная форма: допускает больше настроек, получает доступ к dispatch и, при необходимости, к ownProps
  • Сокращенная объектная форма: более декларативная и простая в использовании.

Примечание. Мы рекомендуем использовать объектную форму mapDispatchToProps , если только вам не нужно каким-то образом настроить поведение отправки(dispatch).

Объявление mapDispatchToProps как функции​

Объявление mapDispatchToProps как функции дает вам максимальную гибкость в настройке функций, которые получает ваш компонент, и того, как они отправляют действия. Вы получаете доступ к dispatch и ownProps . Вы можете использовать эту возможность для написания ваших функций, которые будут вызываться вашими подключенными компонентами.

Аргументы​

dispatch

Функция mapDispatchToProps будет вызываться с dispatch в качестве первого аргумента. Обычно это используется для возврата новых функций, которые вызывают dispatch() внутри себя и передают им либо простой объект действия напрямую, либо результат создателя действия.

const mapDispatchToProps = (dispatch) =>   return   // отправка (dispatch) простых действий increment: () => dispatch( type: 'INCREMENT' >), decrement: () => dispatch( type: 'DECREMENT' >), reset: () => dispatch( type: 'RESET' >), > > 

Вы также, вероятно, захотите передать аргументы создателям действий:

const mapDispatchToProps = (dispatch) =>   return   // явная пересылка аргументов onClick: (event) => dispatch(trackClick(event)),  // неявная пересылка аргументов onReceiveImpressions: (. impressions) => dispatch(trackImpressions(impressions)), > > 

ownProps ( необязателен )

Если ваша функция mapDispatchToProps объявлена с двумя параметрами, она будет вызываться с dispatch в качестве первого параметра и props , переданные подключенному компоненту в качестве второго параметра, и будет повторно вызываться всякий раз, когда подключенный компонент получает новые пропсы.

Это означает, что вместо повторной привязки новых props к отправителям действий при повторном рендеринге компонента, вы можете сделать эту привязку при изменении props вашего компонента.

Привязывается к монтированию компонента

render()   return button onClick=() => this.props.toggleTodo(this.props.todoId)> /> >  const mapDispatchToProps = dispatch =>   return   toggleTodo: todoId => dispatch(toggleTodo(todoId)) > > 

Привязывается к изменению props

render()   return button onClick=() => this.props.toggleTodo()> /> >  const mapDispatchToProps = (dispatch, ownProps) =>   return   toggleTodo: () => dispatch(toggleTodo(ownProps.todoId)) > > 

Возвращаемое значение​

Ваша функция mapDispatchToProps должна возвращать простой объект:

  • Каждое поле объекта станет независимым пропсом вашего компонента и его значение обычно является функцией, отпраляющей(dispatch) действия при вызове.
  • Если вы используете dispatch внутри создателей действий ( в качестве альтернативы действиям из объекта ), то по конвенции следует назвать поле объекта тем же именем что и создателя действий:
const increment = () => ( type: 'INCREMENT' >) const decrement = () => ( type: 'DECREMENT' >) const reset = () => ( type: 'RESET' >)  const mapDispatchToProps = (dispatch) =>   return   // отправка(dispatch) действий возвращаемых из создателей действий increment: () => dispatch(increment()), decrement: () => dispatch(decrement()), reset: () => dispatch(reset()), > > 

Результат функции mapDispatchToProps будет объединён с пропсами подключенного компонента. Вы можете вызвать их, тем самым отправить(dispatch) их действие.

function Counter( count, increment, decrement, reset >)   return ( div> button onClick=decrement>>-/button> span>count>/span> button onClick=increment>>+/button> button onClick=reset>>reset/button> /div> ) > 

(Полный пример с кодом Counter на CodeSandbox)

Определяем функцию mapDispatchToProps с bindActionCreators ​

Ручная обёртка этих функций утомительна, к счастью, Redux предоставляет функцию, упрощающую это.

bindActionCreators превращает объект, значениями которого выступают создатели действий, в объект с теми же ключами, при этом каждый создатель действия оборачивается в вызов dispatch , таким образом они могут быть вызваны напрямую. Посмотрите документацию Redux bindActionCreators

bindActionCreators принимает 2 параметра:

  1. function (создатель действия) или object (каждое поле которого является создателем действия)
  2. dispatch

Функции обёртки, сгенерированные bindActionCreators , будут автоматически пробрасывать их аргументы, поэтому у вас нет необходимости в ручной работе.

import  bindActionCreators > from 'redux'  const increment = () => ( type: 'INCREMENT' >) const decrement = () => ( type: 'DECREMENT' >) const reset = () => ( type: 'RESET' >)  // Привязка создателя действия // Возвращает (. args) => dispatch(increment(. args)) const boundIncrement = bindActionCreators(increment, dispatch)  // Привязка объекта с создателями действий const boundActionCreators = bindActionCreators(  increment, decrement, reset >,  dispatch ) // Возвращает //  // increment: (. args) => dispatch(increment(. args)), // decrement: (. args) => dispatch(decrement(. args)), // reset: (. args) => dispatch(reset(. args)), // > 

Использование bindActionCreators в нашей функции mapDispatchToProps :

import  bindActionCreators > from 'redux' // .  function mapDispatchToProps(dispatch)   return bindActionCreators( increment, decrement, reset >, dispatch) >  // компонент получит props.increment, props.decrement, props.reset connect(null, mapDispatchToProps)(Counter) 

Ручное внедрение dispatch ​

В случае, если аргумент mapDispatchToProps указан, компонент больше не будет получать dispatch по умолчанию. Вы можете получить его, вручную добавив к возвращаемому значению функции mapDispatchToProps , хотя в большинстве случаев вам не нужно этого делать:

import  bindActionCreators > from 'redux' // .  function mapDispatchToProps(dispatch)   return   dispatch, . bindActionCreators( increment, decrement, reset >, dispatch), > > 

Определяем mapDispatchToProps как объект​

Вы видели, что настройка отправки действий Redux в компоненте React выполняется очень похожим образом: определяем создатель действия, оборачиваем его в другую функцию, выглядящую как (…args) => dispatch(actionCreator(…args)) , и передаём функцию обёртки как пропс вашему компоненту.

Поскольку это распространено, connect поддерживает форму «сокращённого объекта» (“object shorthand”) для аргумента mapDispatchToProps : если вы передадите объект, состоящий из создателей действий вместо функции, connect автоматически вызовет bindActionCreators за вас.

Мы рекомендуем всегда использовать форму «сокращённого объекта» (“object shorthand”) для mapDispatchToProps , кроме случаев, когда у вас есть конкретная причина для настройки поведения отправки (dispatch).

Обратите внимание на это:

  • Предполагается, что каждое поле объекта mapDispatchToProps , является создателем действия
  • Ваши компоненты больше не получат dispatch пропсом
// React Redux делает это за вас автоматически: ;(dispatch) => bindActionCreators(mapDispatchToProps, dispatch) 

Следовательно, наш mapDispatchToProps может быть упрощён:

const mapDispatchToProps =   increment,  decrement,  reset, > 

Поскольку фактическое имя переменной зависит от вас, вы можете дать ей имя, например actionCreators , или даже определить объект внутри вызова connect :

import  increment, decrement, reset > from './counterActions'  const actionCreators =   increment,  decrement,  reset, >  export default connect(mapState, actionCreators)(Counter)  // или export default connect(mapState,  increment, decrement, reset >)(Counter) 

Распространённые проблемы​

Почему мой компонент не получает dispatch ?​

Также встречается как

TypeError: this.props.dispatch is not a function 

Это распространённая ошибка, которая случается при попытке вызвать this.props.dispatch , когда dispatch не внедрён в ваш компонент.

dispatch внедряется в ваш компонент исключительно когда:

1. Вы не передаёте mapDispatchToProps

Значение по умолчанию функции mapDispatchToProps — это просто dispatch => (< dispatch >) . Если вы не передадите mapDispatchToProps , то ваш компонент будет получать dispatch .

Другими словами, если вы сделаете:

// Компонент принимает `dispatch` connect(mapStateToProps /** без второго аргумент*/)(Component) 

2. Ваша функция mapDispatchToProps возвращает dispatch

Вы можете вернуть dispatch из вашей функции mapDispatchToProps :

const mapDispatchToProps = (dispatch) =>   return   increment: () => dispatch(increment()), decrement: () => dispatch(decrement()), reset: () => dispatch(reset()),  dispatch, > > 

Или альтернативно с bindActionCreators :

import  bindActionCreators > from 'redux'  function mapDispatchToProps(dispatch)   return   dispatch, . bindActionCreators( increment, decrement, reset >, dispatch), > > 

Существуют дискуссии по поводу передачи dispatch в пользовательские компоненты при указании mapDispatchToProps ( ответ Дэна Абрамова на #255 ). Вы можете прочитать их для дальнейшего понимания текущих намерений по реализации.

Могу ли я вызывать mapDispatchToProps без mapStateToProps в Redux?​

Да. Вы можете пропустить первый параметр указанием undefined или null . Ваш компонент не будет подписываться на хранилище(store) и всё ещё будет получать пропсом dispatch, определённый в mapDispatchToProps .

connect(null, mapDispatchToProps)(MyComponent) 

Могу ли я вызвать store.dispatch ?​

Взаимодействие с хранилищем(store) напрямую из React компонента является антипаттерном, будь то явный импорт магазина или доступ к нему через контекст (смотрите запись Redux FAQ по настройке хранилища (store) для дальнейших деталей). Позвольте connect обработать доступ в хранилище(store) и используйте dispatch , передаваемый в пропсы, для отправки действий.

Ссылки и источники​

Руководства

Связанная документация

Вопросы и ответы

  • Как получить обычный dispatch из this.props , используя connect с Redux?
  • this.props.dispatch равен undefined при указании mapDispatchToProps
  • Не вызывайте store.dispatch , вместо него используйте this.props.dispatch внедрённый функцией connect
  • Могу ли я указать mapDispatchToProps без mapStateToProps в Redux?
  • Redux документация FAQ: React Redux

Redux FAQ: React Redux

Случайные мутации или изменения состояния напрямую являются наиболее частыми причинами того, что компонент не перерисоваывается после отправки экшена. Redux ожидает, что Ваши редюсеры будут обновлять свои состояни “иммутабельно”, что фактически означает, что надо делать копию данных и изменять уже копию. Если Вы возвращаете тот же самый объект в редюсере, Redux полагает, что ничего не изменилось, даже если Вы изменили содержимое объекта. Аналогично React Redux старается улучшить производительность, делая поверхностное сравнение ссылок входных параметров в shouldComponentUpdate , и, если все ссылки остались такими же, возвращает false , пропуская обновление Вашего компонента.

Важно помнить, что всякий раз, когда вы обновляете вложенные значения, Вы должны также вернуть новые копии этих значений в Ваше дерево состояний. Если у Вас state.a.b.c.d , и Вы хотите обновить данные в d , Вам также надо будет вернуть новые копии c , b , a , и state . state tree mutation diagram показывает, как глубокое изменение в дереве меняет весь путь.

Важно понимать, что “иммутабельное обновление данных” не означает, что Вы должны использовать Immutable.js, хотя это конечно вариант. Вы можете делать иммутабельные обновления простых JS-объектов и массивов, используя несколько различных подходов:

  • копирование объектов с использование таких функций, как Object.assign() и _.extend() , а для массивов — slice() и concat() ,
  • использование spread-оператора (. ) из ES6, который предполагается использовать в следующей версии JavaScript,
  • библиотеки, которые оборачивают логику иммутабельного обновления в простые функции.
Дополнительная информация

Документация

  • Поиск неисправностей
  • React Redux: Поиск неисправностей
  • Рецепты: Использование оператора расширения
  • Рецепты: Структурирование редюсеров — Предварительные концепциии
  • Рецепты: Структурирование редюсеров — Паттерны иммутабельного обновления

Статьи

  • Pros and Cons of Using Immutability with React
  • React/Redux Links: Immutable Data

Обсуждения

  • #1262: Immutable data + bad performance
  • React Redux #235: Predicate function for updating component
  • React Redux #291: Should mapStateToProps be called every time an action is dispatched?
  • Stack Overflow: Cleaner/shorter way to update nested state in Redux?
  • Gist: state mutations

Почему мой компонент перерендеривается слишком часто?

React Redux реализует несколько оптимизаций, чтобы обеспечить перерисовоку компонента только тогда, когда это действительно необходимо. Одна из них — поверхностное сравнение объединенных в объект параметров функции mapStateToProps и аргументов mapDispatchToProps , переданных в connect . К несчастью, поверхностное сравнение не помогает в случаях, когда новый массив или ссылка на объект создаются при каждом вызове . Типичным примером может быть сопоставление массива идентификаторов с соответствующими ссылками на объекты, как, например:

const mapStateToProps = (state) =>  return  objects: state.objectIds.map(id => state.objects[id]) > > 

Хотя массив может каждый раз хранить в точности те же ссылки, сам массив имеет другую ссылку, а в этом случае поверхностное сравнение возвращает false , и React Redux запускает перерисовку компонента-обертки.

Излишние перерисовки могут быть решены сохранением массива объектов в состоянии с использованием редюсера, с кэшированием сопоставляемого массива с использованием Reselect, или реализацией shouldComponentUpdate внутри компонента вручную и выполнением более глубокого сравнения параметров с использованием таких функций, как _.isEqual . Будьте осторожны, чтобы не сделать shouldComponentUpdate() более затратным, чем сам рендеринг! Всегда используйте порфилировщик для проверки Ваших предположений касаемо производительности.

Для неподключенных компонентов Вы можете проверять, какие параметры передаются. Общая проблема — это наличие в родительском компоненте привязки функции к контексту внутри рендера, как, например, < Child onClick = /> . Это создает новую функциональную зависимость каждый раз, когда родитель перерисовывается. Считается хорошей практикой привязывать функции к контексту один раз в конструкторе компонента-родителя.

Дополнительная информация

Документация

Статьи

  • A Deep Dive into React Perf Debugging
  • React.js pure render performance anti-pattern
  • Improving React and Redux Performance with Reselect
  • Encapsulating the Redux State Tree
  • React/Redux Links: React/Redux Performance

Обсуждения

Библиотеки

Как я могу ускорить мой mapStateToProps ?

Пока React Redux делает работу по минимизации числа вызовов функции mapStateToProps , все еще надо быть уверенным, что Ваш mapStateToProps запускается быстро и также минимизирует количество выполняемой работы. Общий рекомендуемый подход — это создавать мемоизированные селекторы функций, используя Reselect. Эти селекторы могут быть объединены и составлены вместе, селекторы позже будут запущены только в том случае, если входные данные изменились. Это значит, что Вы можете создать селекторы, которые делают такие вещи, как фильтрация или сортировка, и быть уверенными, что на самом деле они будут работать только когда это нужно.

Дополнительная информация

Документация

Статьи

Обсуждения

  • #815: Working with Data Structures
  • Reselect #47: Memoizing Hierarchical Selectors

Почему у меня недоступен this.props.dispatch в моем подсоединенном компоненте?

Функция connect() получает 2 основных аргумента, оба необязательных. Первый — mapStateToProps — функция, которую Вы предоставляете для чтения данных из стора, при их изменении, и передачи этих значений в качестве агрументов в Ваш компонент. Вторая — mapDispatchToProps — функция, которую Вы предоставляете для возможности использовать dispatch метод стора, обычно создавая предварительно связанные (pre-bound) версии генераторов экшенов, которые будут автоматически отправлять свои экшены как только будут вызваны.

Если Вы не передаете Вашу собственную функцию mapDispatchToProps при вызове connect() , React Redux передаст версию по умолчанию, которая просто возвращает dispatch функцию как параметр. Это означает, что если Вы передаете свою функцию, то dispatch автоматически не передается. Если Вы все еще хотите получить dispatch как аргумент, Вам надо явно возвращать его самим в Вашей реализации mapDispatchToProps .

Дополнительная информация

Документация

Обсуждения

  • React Redux #89: can i wrap multi actionCreators into one props with name?
  • React Redux #145: consider always passing down dispatch regardless of what mapDispatchToProps does
  • React Redux #255: this.props.dispatch is undefined if using mapDispatchToProps
  • Stack Overflow: How to get simple dispatch from this.props using connect w/ Redux?

Должен ли я подключать (connect) только мой корневой компонент или я могу подключить несколько компонентов в моем дереве?

Раньше документация Redux советовала Вам иметь несколько подключаемых компонентов рядом с корневым. Однако, время и опыт показали, что это, как правильно, требует от нескольких компонентов знать слишком много о требованиях к данным всех их потомков, и заставляет их передавать запутывающее количество параметров.

Текущаяя предложенная лучшая практика — это разделять Ваши компоненты на “представления” и “контейнеры” и извлекать подключенный контейнер компонента везде, где это имеет смысл:

Акцент на том, что “один компонент-контейнер в качестве корневого”, в примерах Redux был ошибкой. Не используйте это как афоризм. Пытайтесь хранить представления ваших компонентов отдельно. Создавайте контейнеры, подключая их когда это удобно. Всякий раз, когда Вы чувствуете, что Вы дублируете код родительского компонента, чтобы передать данные нескольким потомкам, пора извлечь контейнер. Как правило, как только вы чувствуете, что родитель знает слишком много о “личных” данных или действиях своих детей, пора извлечь контейнер.

По факту, тесты показывают, что большее число подключенных компонентов, как правило, приводит к улучшению производительности, чем меньшее их количество.

В целом, старайтесь найти баланс между пониманием потока данных и областями ответственности Ваших компонентов.

Дополнительная информация

Документация

  • Basics: Usage with React
  • FAQ: Performance — Scaling

Статьи

  • Presentational and Container Components
  • High-Performance Redux
  • React/Redux Links: Architecture — Redux Architecture
  • React/Redux Links: Performance — Redux Performance

Обсуждения

  • Twitter: emphasizing “one container” was a mistake
  • #419: Recommended usage of connect
  • #756: container vs component?
  • #1176: Redux+React with only stateless components
  • Stack Overflow: can a dumb component use a Redux container?

Объясните на русском языке как работает mapStateToProps в React Redux

Читаю на оф. сайте на английском и не могу понять, как оно точно работает. Как я понимаю(скорее всего не правильно) функция принимает состояние(state) компонента и передает его в хранилище(store) тем самым обновляя store?

Отслеживать
задан 21 ноя 2019 в 19:04
Qui-Gon Jinn Qui-Gon Jinn
309 3 3 серебряных знака 14 14 бронзовых знаков

mapStateToProps, обратите внимание как называется функция. В ней нет слова component => она этого не делает

21 ноя 2019 в 19:32

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Достаточно просто. Помните деструктурирующее присваивание современного JavaScript? Используя этот синтаксис мы можем присвоить только нужные нам поля объекта.

function test() < console.log(a, b, c); // 1, 2, 3 >test(); // d игнорируется 

Тут по аналогии — мы избегаем полного дублирования состояния компоненту, выделяя только нужные нам поля

function mapStateToProps(state: State) < return < visible: !(state.login && state.password), >; > function mapDispatchToProps(dispatch: Redux.Dispatch) < return < login: (login, password) =>dispatch(), >; > export const LoginDialog = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(Internal.LoginDialog); 

Так же, мы можем выразить prop для компонента исходя из нескольких свойств состояния, например, диалог авторизации показывается тогда, когда приложению неизвестны логин и пароль. При этом, сами логин и пароль он точно не знает.

И mapStateToProps, и mapDispatchToProps работают по направлению к компоненту от состояния. Для обновления состояния используется dispatch.

Секретные материалы: тайная сторона Redux становится явной ��

В процессе создания всё более сложных и крупных приложений в React начинаешь понимать, что управление общим состоянием всего приложения невозможно только при помощи класса React.Component , использующего конструктор constructor() с вызовом setState() . Нам нужен контейнер состояний, такой как Redux , чтобы можно было запустить его в разных средах, централизовав состояние/логику приложения, и легко проводить отладку.

Итак, вот некоторые особенности Redux, с которыми вы, наверняка, уже сталкивались, но не совсем понимали, что и как они делают… и, конечно же, примеры.

Мы рассмотрим 15 малоизвестных особенностей Redux:

  1. Ключевые принципы Redux.
  2. Компромиссы при использовании Redux вместо Flux.
  3. Различие между mapStateToProps и mapDispatchToProps .
  4. Диспетчеризация действия в редукторе.
  5. Диспетчеризация действия при загрузке.
  6. Сброс состояния в Redux.
  7. Использование символа @ в функции декораторе connect.
  8. Различие между React context и React-Redux.
  9. Создание AJAX-запроса в Redux.
  10. Лучший способ получить доступ в хранилище Redux.
  11. Зачем использовать константы?
  12. Зачем использовать ownProps в mapStateToProps() и mapDispatchToProps() ?
  13. Различие между call() и put() в Redux-Saga.
  14. Различие между Redux-Saga и Redux-Thunk.
  15. Установка начального состояния в Redux.

1. Ключевые принципы Redux

В основе работы Redux лежат 3 главных принципа:

1️⃣ Единственный источник истины

Это означает, что состояние всего приложения содержится в хранилище в виде дерева объектов.

  • Единственное дерево состояния облегчает процесс отладки и проверки приложения.
  • Позволяет сохранять состояние приложения для ускорения процесса разработки.
  • Облегчает создание универсальных и крупномасштабных приложений.
// При выполнении:
console.log(store.getState())
// В результате вы получите:
visibilityFilter: 'SHOW_ALL',
todos: [
text: 'Consider using Redux',
completed: true,
>,
text: 'Keep all state in a single tree',
completed: false
>
]
>

2️⃣ Состояние только для чтения

Состояние можно изменить только при отправке действия.

Так как все состояния централизованы и происходят последовательно друг за другом, то нет необходимости следить за состоянием гонки.

// Способ отправки действий: 
store.dispatch( type: 'COMPLETE_TODO',
index: 1
>)
store.dispatch( type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_COMPLETED'
>)

3️⃣ Изменения выполняются чистыми функциями

Вы пишите редукторы, чтобы указать на изменение состояния при помощи действий.

Так как редукторы являются чистыми функциями, вы можете контролировать порядок их вызова, отправлять дополнительные данные или даже писать переиспользуемые редукторы.

// Способ создания чистых функций/редукторов 
function visibilityFilter(state = 'SHOW_ALL', action) switch (action.type) case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
>
>
function todos(state = [], action) switch (action.type) case 'ADD_TODO':
return [
. state,
text: action.text,
completed: false
>
]
case 'COMPLETE_TODO':
return state.map((todo, index) => if (index === action.index) return Object.assign(<>, todo, completed: true
>)
>
return todo
>)
default:
return state
>
>
import < combineReducers, createStore >from 'redux'
const reducer = combineReducers(< visibilityFilter, todos >)
const store = createStore(reducer)

2. Преимущества Flux перед Redux

Flux — это шаблон, Redux — библиотека. При использовании Redux вам придется пойти на некоторые компромиссы:

  1. Придется избегать мутаций. Этого можно добиться при помощи пакетов redux-immutable-state-invariant и Immutable.js.
  2. Тщательно выбирать пакеты. У Redux есть точки расширения, такие как мидлвары и расширители хранилища, содержащие обширную экосистему пакетов.
  3. Отсутствует удобная интеграция с Flow.

3. Различие между mapStateToProps() и mapDispatchToProps()

> mapStateToProps()

Это утилита, которая помогает вашему компоненту обновлять состояние (которое обновляется некоторыми другими компонентами). Эта функция передается в качестве первого аргумента в connect и впоследствии будет вызываться каждый раз при изменении состояния хранилища Redux.

// TodoList.js
function mapStateToProps(state) const < todos >= state
return < todoList: todos.allIds >
>
export default connect(mapStateToProps)(TodoList)

> mapDispatchToProps()

Это утилита, которая помогает вашему компоненту запускать событие действия (отправка действия, которое может вызвать изменение состояния приложения). Компонент получает [dispatch](https://react-redux.js.org/api/connect#dispatch) по умолчанию.

import < addTodo, deleteTodo, toggleTodo >from './actionCreators'const mapDispatchToProps = addTodo, 
deleteTodo,
toggleTodo
>
export default connect(
null,
mapDispatchToProps
)(TodoApp)

4. Диспетчеризация действия в редукторе

Причиной для внесения этого пункта в мой список стал следующий популярный вопрос со Stackoverflow: “Можно ли отправить действие в самом редукторе?” Отвечая на этот вопрос, скажу лишь одно:

Отправка действия в редуктор — это анти-шаблон.

5. Диспетчеризация действия при загрузке

Вы можете отправить действие во время загрузки с помощью метода componentDidMount() , проверяя данные в методе render() .

class App extends Component componentDidMount() this.props.fetchData() 
>
render() return this.props.isLoaded
?
:
>
>
const mapStateToProps = (state) => ( isLoaded: state.isLoaded
>)
const mapDispatchToProps = export default connect(mapStateToProps, mapDispatchToProps)(App)

6. Сброс состояния в Redux

Лучший способ — использовать исходный редуктор и передать действие редуктору, созданному при помощи combineReducers() .

const appReducer = combineReducers( // Здесь размещаются редукторы высшего уровня.
>)
const rootReducer = (state, action) => if (action.type === 'USER_LOGOUT') state = undefined
>
return appReducer(state, action)
>

7. Использование символа @ в функции декораторе connect

В JavaScript символ @ используется для обозначения декораторов, при помощи которых вы добавляете или видоизменяете классы и свойства.

8. Различие между React context и React-Redux

> React context

React context предоставляет способ направлять данные через дерево компонентов без необходимости передачи свойств сверху вниз вручную на каждом уровне.

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

> React-Redux

React-Redux — это официальная библиотека, которая предоставляет привязки React для Redux. Она позволяет компонентам React считывать данные из хранилища Redux и отправлять туда действия для обновления данных.

9. Создание AJAX-запроса в Redux

AJAX позволяет отправлять асинхронные HTTP-запросы для передачи и извлечения данных с сервера.

Чтобы выполнить вызов AJAX в Redux, можно использовать мидлвар redux-thunk для определения асинхронных действий.

export function fetchAccount(id) return dispatch => dispatch(setLoadingAccountState()) // Отображение спиннера загрузки 
fetch(`/account/$`, (response) => dispatch(doneFetchingAccount()) // Скрытие спиннера загрузки
if (response.status === 200) dispatch(setAccount(response.json)) // Использование обычной функции для установки полученного состояния
> else dispatch(someError)
>
>)
>
>
function setAccount(data) return < type: 'SET_Account', data: data >
>

10. Лучший способ получить доступ в хранилище Redux

Лучший способ — использовать функцию connect() с применением шаблона функций высшего порядка. Это позволяет отобразить креаторы состояния и действия в компонент и автоматически передать их, когда обновится хранилище.

// Вы можете передать контекст как опцию в функцию connect 
export default connect(
mapState,
mapDispatch,
null,
< context: MyContext >
)(MyComponent)
// или, как обычно, вызвать функцию connect, чтобы начать
const ConnectedComponent = connect(
mapState,
mapDispatch
)(MyComponent)
// Затем передайте настроенный контекст как свойство присоединенному компоненту
/>

11. Зачем использовать константы?

Использование констант позволяет легко находить все случаи применения конкретной выполняемой функции в проекте. Кроме того, они помогут избежать большого количества ошибок ReferenceError .

// В constants.js
export const ADD_TODO = 'ADD_TODO'
export const DELETE_TODO = 'DELETE_TODO'
export const EDIT_TODO = 'EDIT_TODO'
// В actions.js
import < ADD_TODO >from './actionTypes';
export function addTodo(text) return < type: ADD_TODO, text >
>
// В reducer.js
import < ADD_TODO >from './actionTypes'
export default (state = [], action) => switch (action.type) case ADD_TODO:
return [
. state,
text: action.text,
completed: false
>
];
default:
return state
>
>

12. Зачем использовать ownProps в mapStateToProps() и mapDispatchToProps() ?

ownProps является необязательным параметром, который мы добавляем в mapStateToProps() или mapDispatchToProps() в качестве второго аргумента. Используйте его в том случае, если вашему компоненту нужны данные из его собственных свойств для извлечения данных из хранилища.

// Todo.js
function mapStateToProps(state, ownProps) const < visibilityFilter >= state
const < id >= ownProps
const todo = getTodoById(state, id)
// компонент получает дополнительно:
return < todo, visibilityFilter >
>
// Затем в вашем приложении родительский компонент отображает:

// и ваш компонент получает props.id, props.todo и props.visibilityFilter

Вам необязательно включать значения из ownProps в объект, возвращаемый из mapStateToProps . connect автоматически объединит разные источники свойств в конечный набор.

13. Различие между call() и put() в Redux-Saga

redux-saga — это библиотека, которая призвана облегчить управление побочными эффектами приложения, а также сделать их более эффективными в выполнении, легкими в тестировании и способствующими улучшению обработки ошибок.

> call()

Проще говоря, вы используете call() для создания описания эффекта, который дает команду мидлвару для вызова промиса. После этого мидлвар вызывает функцию и проверяет ее результат.

> put()

Что же касается функции put() , то она создает эффект, который дает мидлвару команду отправить действие в хранилище.

function* fetchUserSaga(action) // Функция `call` получает оставшиеся аргументы, которые передаются в функцию `api.fetchUser`. 
// Команда мидлвару вызвать промис, его разрешенное значение присваивается переменной `userData`
const userData = yield call(api.fetchUser, action.userId)
// Команда мидлвару отправить соответствующее действие.
yield put( type: 'FETCH_USER_SUCCESS',
userData
>)
>

Таким образом, call() и put() являются функциями креаторов эффектов.

14. Различие между Redux-Saga и Redux-Thunk

Мидлвар Redux Thunk позволяет написать креатор действия, который возвращает функцию вместо действия. Thunk можно использовать для задержки отправки действия или для отправки только при выполнении определенного условия.

  • Thunk использует промисы, тогда как Saga — генераторы.
  • Thunk прост в использовании, да и промисы хорошо знакомы. Saga/Генераторы более мощные, но требуют изучения.

15. Установка начального состояния в Redux

Существуют 2 способа:

1.Использовать метод createStore , который принимает в качестве второго аргумента необязательное значение preloadedState .

const rootReducer = combineReducers( todos: todos, 
visibilityFilter: visibilityFilter
>)
const initialState = todos: [< id: 123, name: 'example', completed: false >]
>
const store = createStore(
rootReducer,
initialState
)

2.Использовать явную проверку внутри редуктора.

function myReducer(state = someDefaultValue, action)

Надеюсь, я понятно объяснил эти 15 особенностей Redux. Интересно, а знали ли вы о них?

  • Увеличиваем производительность приложения React + Redux с библиотекой Reselect
  • Что такое Редьюсеры: Как использовать их без Redux
  • Анализ возможностей React Native или 15 вариантов улучшить ваше приложение!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *