Что такое mapDispatchToProps?

358

Я читал документацию для библиотеки Redux, и у нее есть этот пример:

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

Это на самом деле не имеет смысла. Зачем вам нужно, mapDispatchToPropsкогда у вас уже есть mapStateToProps?

Они также предоставляют этот удобный пример кода:

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

Может кто-нибудь объяснить, в терминах непрофессионала, что это за функция и почему она полезна?

Код Whisperer
источник

Ответы:

578

Я чувствую, что ни один из ответов не раскрылся, почему mapDispatchToPropsэто полезно.

На это действительно можно ответить только в контексте container-componentшаблона, который я нашел наиболее понятным при первом прочтении: компоненты контейнера, затем использование с React .

Короче говоря, вы componentsдолжны заниматься только показом вещей. Единственное место , где они должны получить информацию от является их реквизитом .

Отдельно от «отображения материала» (компонентов) есть:

  • как вы получаете материал для отображения,
  • и как вы обрабатываете события.

Вот для чего containers.

Следовательно, «хорошо разработанный» componentшаблон выглядит так:

class FancyAlerter extends Component {
    sendAlert = () => {
        this.props.sendTheAlert()
    }

    render() {
        <div>
          <h1>Today's Fancy Alert is {this.props.fancyInfo}</h1>
          <Button onClick={sendAlert}/>
        </div>
     }
}

Посмотрите , как этот компонент получает информацию он отображает из реквизита (который пришел из Redux магазина через mapStateToProps) , и он также получает функцию действия от ее реквизита: sendTheAlert().

Вот где mapDispatchToPropsприходит: в соответствующемcontainer

// FancyButtonContainer.js

function mapDispatchToProps(dispatch) {
    return({
        sendTheAlert: () => {dispatch(ALERT_ACTION)}
    })
}

function mapStateToProps(state) {
    return({fancyInfo: "Fancy this:" + state.currentFunnyString})
}

export const FancyButtonContainer = connect(
    mapStateToProps, mapDispatchToProps)(
    FancyAlerter
)

Интересно, видите ли вы, что теперь это container 1, который знает о редуксе, диспетчеризации, хранении, состоянии и ... вещах.

componentВ шаблоне, FancyAlerter, что делает рендеринг не нужно знать о какой - либо из этого материала: он получает свой метод для вызова на onClickкнопки, с помощью его реквизита.

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

Все это очень похоже на пример todo в документах и ​​другой ответ здесь, но я попытался привести его в свете схемы, чтобы подчеркнуть почему .

(Примечание: вы не можете использовать mapStateToPropsдля той же цели, что и mapDispatchToPropsпо основной причине, к которой у вас нет доступа dispatchвнутрь mapStateToProp. Поэтому вы не можете использовать, mapStateToPropsчтобы предоставить завернутому компоненту метод, который использует dispatch.

Я не знаю, почему они решили разбить его на две функции отображения - возможно, было бы лучше иметь mapToProps(state, dispatch, props) одну функцию IE для выполнения обеих!


1 Обратите внимание, что я намеренно явно назвал контейнер FancyButtonContainer, чтобы подчеркнуть, что это «вещь» - идентичность (и, следовательно, существование!) Контейнера как «вещь» иногда теряется в стенографии

export default connect(...) ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

синтаксис, который показан в большинстве примеров

GreenAsJade
источник
5
Я все еще хотел бы знать: что относительно требуемой функциональности не может быть обработано прямым вызовом store.dispatch из компонента?
pixelpax
8
@ user2130130 Нет. Это о хорошем дизайне. Если вы подключили свой компонент к store.dispatch, то, когда вы решите выделить избыточный код или захотите использовать его в каком-либо месте, не основанном на Redxu (или о чем-то другом, о чем я сейчас не могу думать), вы застряли с много изменений. Ваш вопрос в целом звучит так: «Почему мы беспокоимся о« хороших методах проектирования »- вы получаете те же функциональные возможности, какими вы их кодируете». Предоставляется mapDispatchToProps, чтобы вы могли писать хорошо спроектированные, четко отделенные компоненты.
GreenAsJade,
4
Что я действительно не понимаю, так это то, что это: ALERT_ACTIONфункция действия или функция, typeкоторая возвращается из функции действия? : / так озадачен
Джейми Хатбер
1
@JamieHutber Строго говоря, ALERT_ACTION не имеет ничего общего с этим вопросом. Это допустимый аргумент для отправки, и, как это происходит, в моем коде он исходит от «компоновщика действий», который возвращает объект, который использует диспетчеризация, как описано redux.js.org/docs/api/Store.html#dispatch , Основной момент здесь заключается в том, что способ вызова диспетчеризации описан в контейнере и передается на реквизиты компонентов. Компонент получает функции только из своих реквизитов, больше нигде. «Неправильный» способ сделать это в этом шаблоне - передать диспетчеру компонент и выполнить тот же вызов в компоненте.
GreenAsJade
1
Если у вас есть несколько создателей действий, которые необходимо передать дочернему компоненту в качестве реквизита, одна из альтернатив - заключить их в bindActionCreators внутри mapDispatchToProps (см. Redux.js.org/docs/api/bindActionCreators.html ); другая альтернатива - просто предоставить объект создателей действий для connect (), например connect (mapStateToProps, {actioncreators}), response-redux обернет их dispatch () для вас.
Дэйва
81

Это в основном сокращение. Поэтому вместо того, чтобы писать:

this.props.dispatch(toggleTodo(id));

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

this.props.onTodoClick(id);

или, скорее всего, в этом случае вы бы использовали это в качестве обработчика событий:

<MyComponent onClick={this.props.onTodoClick} />

Вот полезное видео Дана Абрамова об этом здесь: https://egghead.io/lessons/javascript-redux-generating-containers-with-connect-from-react-redux-visibletodolist

hamstu
источник
Спасибо. Мне также интересно, как диспетчеризация добавляется в реквизит?
Код Whisperer
14
Если вы не предоставите свою собственную mapDispatchфункцию, Redux будет использовать значение по умолчанию. Эта mapDispatchфункция по умолчанию просто берет dispatchссылку на функцию и дает ее вам как this.props.dispatch.
маркериксон
7
Метод отправки передается Компонентом Провайдера
Диего Хаз
55

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

Сайсурья Каттамури
источник
23

mapStateToProps, mapDispatchToPropsа connectиз react-reduxбиблиотеки предоставляет удобный способ доступа к вашему stateи dispatchфункции вашего магазина. Таким образом, по сути, соединение - это компонент высшего порядка, вы также можете подумать, что это обертка, если это имеет смысл для вас. Таким образом, каждый раз, когда ваше stateизменение mapStateToPropsбудет изменено, будет вызываться ваш новый stateи впоследствии, когда вы propsобновляете компонент, будет запускаться функция рендеринга для отображения вашего компонента в браузере. mapDispatchToPropsтакже хранит значения ключей propsвашего компонента, обычно они принимают форму функции. Таким образом, вы можете вызвать stateизменения из вашего компонента onClick, onChangeсобытия.

Из документов:

const TodoListComponent = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

function toggleTodo(index) {
  return { type: TOGGLE_TODO, index }
}

const TodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList) 

Также убедитесь, что вы знакомы с функциями React без состояния и компонентами высшего порядка.

Влад Филимон
источник
Таким образом, отправка походит на событие в принципе?
Код Whisperer
2
Это может быть связано с событием, диспетчерская просто функция , и единственный способ изменить прикладную состояние . mapStateToProps - это один из способов представить функцию отправки вашего магазина в React Component. Также обратите внимание, что connect не является частью redux, на самом деле это всего лишь утилита, и шаблонная библиотека под названием response-redux сокращает работу для реагирования и redux. Вы можете достичь того же результата без реакции-избыточности, если передадите свой магазин от корневого компонента реакции дочерним элементам.
Влад Филимон
3

mapStateToPropsполучает stateи propsи позволяет извлечь реквизит из состояния для передачи компоненту.

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

Я считаю, что это только избавляет вас от необходимости делать dispatch(actionCreator())внутри вашего компонента, что делает его немного легче для чтения.

https://github.com/reactjs/react-redux/blob/master/docs/api.md#arguments

Гарри Морено
источник
Спасибо, но какова ценность dispatchметода? Откуда это взялось?
Код Whisperer
ой. Диспетчеризация в основном заставляет действие запускать однонаправленный поток притока / потока. Другие ответы, кажется, ответили на ваш вопрос лучше, чем этот.
Гарри Морено
Кроме того, способ отправки происходит от расширения, предоставляемого <Provider/>самим. Он выполняет свою собственную магию компонентов высокого порядка, чтобы гарантировать, что отправление доступно в его дочерних компонентах.
Рикардо
3

Теперь предположим, что для редукса есть действие:

export function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

Когда вы импортируете его,

import {addTodo} from './actions';

class Greeting extends React.Component {

    handleOnClick = () => {
        this.props.onTodoClick(); // This prop acts as key to callback prop for mapDispatchToProps
    }

    render() {
        return <button onClick={this.handleOnClick}>Hello Redux</button>;
    }
}

const mapDispatchToProps = dispatch => {
    return {
      onTodoClick: () => { // handles onTodoClick prop's call here
        dispatch(addTodo())
      }
    }
}

export default connect(
    null,
    mapDispatchToProps
)(Greeting);

Как говорит название функции mapDispatchToProps(), сопоставьте dispatchдействие с реквизитом (реквизит нашего компонента)

Таким образом, опора onTodoClickявляется ключом к mapDispatchToPropsфункции, которая делегирует дальнейшие действия addTodo.

Также, если вы хотите обрезать код и обойти ручную реализацию, то вы можете сделать это,

import {addTodo} from './actions';
class Greeting extends React.Component {

    handleOnClick = () => {
        this.props.addTodo();
    }

    render() {
        return <button onClick={this.handleOnClick}>Hello Redux</button>;
    }
}

export default connect(
    null,
    {addTodo}
)(Greeting);

Что именно означает

const mapDispatchToProps = dispatch => {
    return {
      addTodo: () => { 
        dispatch(addTodo())
      }
    }
}
Познакомьтесь с Завери
источник