Перейти к содержимому

Mapdispatchtoprops redux что это

  • автор:

Объясните на русском языке как работает 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.

React Redux. Получение доступа к state из функции mapDispatchToProps()

Всем привет! Сегодня рассмотрим решение, довольно-таки популярной проблемы — получение доступа к state из функции mapDispatchToProps() react-redux приложения.

Имеется типовой компонент-контейнер (про идеологию компонентов react-redux можно почитать здесь), который генерирую с помощью функции connect(). Код представлен ниже (публикую кусок кода, относящийся к данной теме):

const mapStateToProps = (state) => < return state.play; >; const mapDispatchToProps = (dispatch) => < return < togglePlay: () => < dispatch(togglePlay()); >> >; const ButtonPlayComponentContainer = connect( mapStateToProps, mapDispatchToProps )(ButtonPlayComponentView);

Тут все просто, определяем функции mapStateToProps() для чтения состояния и mapDispatchToProps() для передачи события. Далее генерируем компонент путем передачи созданных функций в connect().

В добавок публикую код метода render() компонента-представления, для более ясной картины:

render() < return( 
> >
); >;

Обычная ситуация, при клике на кнопку, меняется state и в зависимости от состояния, меняется класс у элемента.

Но теперь появляется задача, при изменении состояния, возвращать ту или иную функцию. Вроде бы не сложно, проблема решается одним if, но есть одно но. У нас нет доступа к state в методе mapDispatchToProps(). С лету, на ум приходит сразу один вариант — сделать запрос к хранилищу с помощью метода getState() и получить текущее состояние. Но такой вариант смутил меня своей бестолковостью. Ибо пропадает весь смысл в функции mapStateToProps, которая и так отвечает за состояние.

Просмотрев документацию по методу connect() (на этот раз внимательно), обнаружил параметр mergeProps:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]);

Выдержка из документации по данному параметру:

You may specify this function to select a slice of the state based on props, or to bind action creators to a particular variable from props.

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

Немного погуглив, по теме реализации метода mergeProps, наткнулся на вопрос на github.

В итоге, получаем:

const mapStateToProps = (state) => < return state.play; >; const mergeProps = (stateProps, dispatchProps) => < const < play >= stateProps; const < dispatch >= dispatchProps; const toggle = () => < dispatch(togglePlay()); if (play != true) < this.playAction(); >else < this.stopAction(); >>; return < play: play, togglePlay: () => < toggle(); >>; >; const ButtonPlayComponentContainer = connect( mapStateToProps, null, mergeProps )(ButtonPlayComponentView);

Тут тоже все просто, в mergeProps прилетают stateProps, который содержит текущее состояние и dispatchProps, который дает возможность отправить событие. Далее по коду делаем проверку на состояние, результатом которой будет нужная функция и возвращаем объект с текущим state и событием, который благополучно попадет в props нашего компонента-представления.

Если обнаружили какие-нибудь недочеты, пишите, поправлю. Спасибо за внимание.

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?

Что такое mapDispatchToProps?

Всем привет. Не до конца понимаю зачем нужно mapDispatchToProps.

Допустим у меня есть action. В этом action у меня есть action creator и ф-ция которая делает dispatch.
Я импортирую ту функцию (которая делает диспатч) в компонент и в нужный момент вызываю ее.

Объясните пожалуйста, если можна на маленьком примере.

UPD:
После просмотра этого видео и прочтения комента от Антон Спирин все стало понятно, спасибо!

  • Вопрос задан более трёх лет назад
  • 6316 просмотров

1 комментарий

Простой 1 комментарий

dimmond_pro

Dmitriy Uvarov @dimmond_pro
Решения вопроса 2

nakree

Fullstack Developer

Сам недавно разбирался с этим, и пришел к такому пониманию (возможно оно не верное):
Компонент изначально ничего не знает про redux, редюсеры и экшены. Потому мы не можем просто вызвать dispatch с каким то экшеном (будет ошибка мол такая функция неопределенна)
Чтобы мы получили эту возможность, мы просим редакс через mapDispatchToProps, прислать нам эту функцию в виде колбека через пропсы. То есть мы получаем возможность сделать диспатч, а доступ к этой функции у нас есть через пропсы.

Ответ написан более трёх лет назад
Комментировать
Нравится 1 Комментировать

rockon404

Антон Спирин @rockon404 Куратор тега React
Frontend Developer

Это объект или функция передаваемая в connect вторым аргументом.
Если вы передаете в connect вторым аргументом объект с AC, то connect оборачивает ваши AC в функцию-обертку () => store.dispatch(AC) и передаёт в props компонента. Если передаете вторым аргументом функцию, connect выполняет ее, передавая на вход первым аргуменом store.dispatch, вторым props компонента, и передаёт в props компонента результат выполнения. Все.

Ответ написан более трёх лет назад
Нравится 1 10 комментариев
WarriorKodeK @WarriorKodeK Автор вопроса

Простите что задаю тупые вопросы, но могли бы вот на этом примере обьяснить какую функцию выполняет mapdispatchtoprops

action

export const getConditionById = (domenKey, apiKey, id) => < const domenPath = domen[domenKey], apiPath = api[apiKey]; return Api.get(`$$/$`).then(res => < if (res) < const < data >= res.data; const body = < . data >; store.dispatch(< type: `$_SET_COND_QUESTION`, payload: < body >>); return data; > >); >; export const clearCreateQuestion = () => store.dispatch(< type: `$_CLEAR` >);

Я импортирую его в компонент (используя его там)

import

import < clearCreateQuestion, getConditionById >from "../../../../actions";
const mapDispatchToProps = dispatch => bindActionCreators(< dispatch >, dispatch); export default connect(mapStateToProps, mapDispatchToProps)( CreateConditionComponent );

rockon404

Антон Спирин @rockon404 Куратор тега React

WarriorKodeK, ох. Во-первых установите и подкючите redux-thunk это отличное middleware для асинхронных действий.
Ваше действие:

export const getConditionById = async dispatch => < const domenPath = domen[domenKey]; const apiPath = api[apiKey]; try < const < data >= await Api.get(`$$/$`); dispatch(< type: `$_SET_COND_QUESTION`, payload: < body: data >, >); return data; > catch (e) < console.log(e); >>; export const clearCreateQuestion = () => (< type: `$_CLEAR` >);
const mapDispatchToProps = < clearCreateQuestion, getConditionById, >; export default connect(mapStateToProps, mapDispatchToProps)(CreateConditionComponent);

Самому со store.dispatch работать не надо, connect обернет в него все вызовы за вас. Когда передаете в mapDispatchToProps объект(как в моем приере) он передается в вызов bindActionCreators.

WarriorKodeK @WarriorKodeK Автор вопроса

Антон Спирин, Смотрите, правильно ли я понял, что в mapDispatchToProps, делается для тех ф-ций которые как-то зависисят от пропсов или ф-ции которые имеют в себе dispatch?

rockon404

Антон Спирин @rockon404 Куратор тега React

WarriorKodeK, mapDispatchToProps используется для передачи AC в props компонента в виде функций которые просто вызывать.

componentDidMount() < const < id, getConditionById >= this.props; getConditionById(id); >

Но да вы можете использовать воторой аргумент ownProps и передавать в mapDispatchToProps props компонента. Это редкий кейс. Я в проектах ни разу его не использовал. В mapStateToProps второй аргумент используется часто.

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

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