[Из песочницы] 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 нашего компонента-представления.
Если обнаружили какие-нибудь недочеты, пишите, поправлю. Спасибо за внимание.
Комментарии (1)
7 ноября 2016 в 14:33
0↑
↓
В новой версии react-redux эта проблема и другие похожие решается более элегантно через
connectAdvanced(selectorFactory, [connectOptions])
https://github.com/reactjs/react-redux/blob/next/docs/api.md#connectadvancedselectorfactory-connectoptions
Там можно более гибко конфигурировать props для вложенного компонента.Пример кода:
import * as actionCreators from './actionCreators' import { bindActionCreators } from 'redux' function selectorFactory(dispatch) { let state = {} let ownProps = {} let result = {} const actions = bindActionCreators(actionCreators, dispatch) const addTodo = (text) => actions.addTodo(ownProps.userId, text) return (nextState, nextOwnProps) => { const todos = nextState.todos[nextProps.userId] const nextResult = { ...nextOwnProps, todos, addTodo } state = nextState ownProps = nextOwnProps if (!shallowEqual(result, nextResult)) result = nextResult return result } } export default connectAdvanced(selectorFactory)(TodoApp)