Skip to the content.

Connect: 使用mapDispatchToProps来调度操作

作为传入connect的第二个参数,mapDispatchToProps用于将操作分派到存储。

As the second argument passed in to connect, mapDispatchToProps is used for dispatching actions to the store.

调度是Redux store的一个函数。你调用store.dispatch派发一个action。这是触发状态更改的唯一方法。

dispatch is a function of the Redux store. You call store.dispatch to dispatch an action. This is the only way to trigger a state change.

使用React Redux,您的组件永远不会直接访问存储区 - connect为您做到了这一点。React Redux提供了两种让组件分派动作的方法:

mapDispatchToProps函数通常简称为mapDispatch,但实际使用的变量名可以是您想要的任何名称。

The mapDispatchToProps functions are normally referred to as mapDispatch for short, but the actual variable name used can be whatever you want.

方法调度

默认:作为Prop调度

如果没有为connect()指定第二个参数,组件将默认接收分派。例如:

If you don’t specify the second argument to connect(), your component will receive dispatch by default. For example:

connect()(MyComponent)
// which is equivalent with
connect(null, null)(MyComponent)

// or
connect(mapStateToProps /** no second argument */)(MyComponent)

一旦以这种方式连接了组件,组件就会接收props.dispatch。您可以使用它将操作分派到store

Once you have connected your component in this way, your component receives props.dispatch. You may use it to dispatch actions to the 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.increment(),而不是调用props.dispatch(() => increment())。有几个原因可以解释为什么你想这样做。

Providing a mapDispatchToProps allows you to specify which actions your component might need to dispatch. It lets you provide action dispatching functions as props. Therefore, instead of calling props.dispatch(() => increment()), you may call props.increment() directly. There are a few reasons why you might want to do that.

更多的声明

首先,将分派逻辑封装到函数中使实现更具声明性。分派一个操作并让Redux store处理数据流是如何实现该行为,而不是它做什么。

First, encapsulating the dispatch logic into function makes the implementation more declarative. Dispatching an action and letting the Redux store handle the data flow is how to implement the behavior, rather than what it does.

一个很好的例子就是在单击按钮时分派操作。直接连接按钮在概念上可能没有意义,拥有按钮参考分派也没有意义。

A good example would be dispatching an action when a button is clicked. Connecting the button directly probably doesn’t make sense conceptually, and neither does having the button reference dispatch.

// button needs to be aware of "dispatch"
<button onClick={() => dispatch({ type: "SOMETHING" })} />

// button unaware of "dispatch",
<button onClick={doSomething} />

一旦你用分派动作的函数包装了所有的动作创建者,组件就不需要分派了。因此,如果您定义自己的mapDispatchToProps,则连接的组件将不再接收分派。

Once you’ve wrapped all our action creators with functions that dispatch the actions, the component is free of the need of dispatch. Therefore, if you define your own mapDispatchToProps, the connected component will no longer receive dispatch.

将动作分派逻辑传递给(未连接的)子组件

此外,您还可以将操作分派函数传递给子组件(可能是未连接的)。这允许更多的组件分派操作,同时保持它们“unawareRedux

In addition, you also gain the ability to pass down the action dispatching functions to child ( likely unconnected ) components. This allows more components to dispatch actions, while keeping them “unaware” of Redux.

// pass down toggleTodo to child component
// making Todo able to dispatch the toggleTodo action
const TodoList = ({ todos, toggleTodo }) => (
  <div>
    {todos.map((todo) => (
      <Todo todo={todo} onClick={toggleTodo} />
    ))}
  </div>
)

这就是React Reduxconnect所做的——它封装了与Redux store对话的逻辑,让你不必担心它。这是你在实现中应该充分利用的。

This is what React Redux’s connect does — it encapsulates the logic of talking to the Redux store and lets you not worry about it. And this is what you should totally make full use of in your implementation.

mapDispatchToProps的两种形式

mapDispatchToProps参数有两种形式。虽然函数形式允许更多的定制,但对象形式很容易使用。

The mapDispatchToProps parameter can be of two forms. While the function form allows more customization, the object form is easy to use.

⭐注意:我们推荐使用mapDispatchToProps的对象形式,除非你特别需要以某种方式自定义分派行为。

⭐ Note: We recommend using the object form of mapDispatchToProps unless you specifically need to customize dispatching behavior in some way.

将mapDispatchToProps定义为函数

mapDispatchToProps定义为函数可以让您在定制组件接收的函数以及它们如何分派操作时拥有最大的灵活性。你可以访问dispatchownProps。您可以利用这个机会编写由连接的组件调用的自定义函数。

Defining mapDispatchToProps as a function gives you the most flexibility in customizing the functions your component receives, and how they dispatch actions. You gain access to dispatch and ownProps. You may use this chance to write customized functions to be called by your connected components.

Arguments

  1. dispatch
  2. ownProps (optional)

dispatch

mapDispatchToProps函数将以dispatch作为第一个参数被调用。你通常会通过返回在自身内部调用dispatch()的新函数来使用它,或者直接传入一个普通的动作对象,或者传入一个动作创建者的结果。

The mapDispatchToProps function will be called with dispatch as the first argument. You will normally make use of this by returning new functions that call dispatch() inside themselves, and either pass in a plain action object directly or pass in the result of an action creator.

const mapDispatchToProps = (dispatch) => {
  return {
    // dispatching plain actions
    increment: () => dispatch({ type: 'INCREMENT' }),
    decrement: () => dispatch({ type: 'DECREMENT' }),
    reset: () => dispatch({ type: 'RESET' }),
  }
}

你也可能想要把参数转发给你的action creators:

You will also likely want to forward arguments to your action creators:

const mapDispatchToProps = (dispatch) => {
  return {
    // explicitly forwarding arguments
    onClick: (event) => dispatch(trackClick(event)),

    // implicitly forwarding arguments
    onReceiveImpressions: (...impressions) =>
      dispatch(trackImpressions(impressions)),
  }
}

ownProps ( optional )

如果您的mapDispatchToProps函数被声明为接受两个参数,那么它将被调用,dispatch作为第一个参数,props作为第二个参数传递给连接的组件,并且当连接的组件接收到新的props时将被重新调用。

If your mapDispatchToProps function is declared as taking two parameters, it will be called with dispatch as the first parameter and the props passed to the connected component as the second parameter, and will be re-invoked whenever the connected component receives new props.

这意味着,当组件的道具发生变化时,你可以不需要在组件重新渲染时重新绑定新的道具到动作调度程序。

This means, instead of re-binding new props to action dispatchers upon component re-rendering, you may do so when your component’s props change.

组件挂载绑定

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))
  }
}

Return

你的mapDispatchToProps函数应该返回一个普通对象:

const increment = () => ({ type: 'INCREMENT' })
const decrement = () => ({ type: 'DECREMENT' })
const reset = () => ({ type: 'RESET' })

const mapDispatchToProps = (dispatch) => {
  return {
    // dispatching actions returned by action creators
    increment: () => dispatch(increment()),
    decrement: () => dispatch(decrement()),
    reset: () => dispatch(reset()),
  }
}

mapDispatchToProps函数的返回将作为props合并到已连接的组件中。你可以直接调用他们来分派它的行动。

The return of the mapDispatchToProps function will be merged to your connected component as props. You may call them directly to dispatch its action.

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>
  )
}

用bindactioncreator定义mapDispatchToProps函数

手工包装这些函数非常繁琐,因此Redux提供了一个函数来简化这一过程。

Wrapping these functions by hand is tedious, so Redux provides a function to simplify that.

bindactioncreator将值为操作创建者的对象转换为具有相同键的对象,但将每个操作创建者包装到一个分派调用中,以便可以直接调用它们。请参阅关于bindactioncreatorRedux文档

bindActionCreators turns an object whose values are action creators, into an object with the same keys, but with every action creator wrapped into a dispatch call so they may be invoked directly. See Redux Docs on bindActionCreators

bindactioncreator接受两个参数:

bindactioncreator生成的包装器函数将自动转发它们的所有参数,因此您不需要手工转发。

The wrapper functions generated by bindActionCreators will automatically forward all of their arguments, so you don’t need to do that by hand.

import { bindActionCreators } from 'redux'

const increment = () => ({ type: 'INCREMENT' })
const decrement = () => ({ type: 'DECREMENT' })
const reset = () => ({ type: 'RESET' })

// binding an action creator
// returns (...args) => dispatch(increment(...args))
const boundIncrement = bindActionCreators(increment, dispatch)

// binding an object full of action creators
const boundActionCreators = bindActionCreators(
  { increment, decrement, reset },
  dispatch
)
// returns
// {
//   increment: (...args) => dispatch(increment(...args)),
//   decrement: (...args) => dispatch(decrement(...args)),
//   reset: (...args) => dispatch(reset(...args)),
// }

在我们的mapDispatchToProps函数中使用bindactioncreator:

To use bindActionCreators in our mapDispatchToProps function:

import { bindActionCreators } from 'redux'
// ...

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ increment, decrement, reset }, dispatch)
}

// component receives props.increment, props.decrement, props.reset
connect(null, mapDispatchToProps)(Counter)

手动注入dispatch

如果提供了mapDispatchToProps参数,组件将不再接收默认的分派。你可以手动将它添加到mapDispatchToProps的返回值中,但大多数时候你不需要这样做:

If the mapDispatchToProps argument is supplied, the component will no longer receive the default dispatch. You may bring it back by adding it manually to the return of your mapDispatchToProps, although most of the time you shouldn’t need to do this:

import { bindActionCreators } from 'redux'
// ...

function mapDispatchToProps(dispatch) {
  return {
    dispatch,
    ...bindActionCreators({ increment, decrement, reset }, dispatch),
  }
}

将mapDispatchToProps定义为一个对象

你已经看到在React组件中调度Redux actions的设置遵循一个非常相似的过程:定义一个动作创建者,将它包装在另一个看起来像(…args) => dispatch(actionCreator(…args))的函数中,并将这个包装函数作为一个道具传递给你的组件。

You’ve seen that the setup for dispatching Redux actions in a React component follows a very similar process: define an action creator, wrap it in another function that looks like (…args) => dispatch(actionCreator(…args)), and pass that wrapper function as a prop to your component.

因为这很常见,connect支持mapDispatchToProps参数的“对象简写”形式:如果你传递的是一个充满动作创建者的对象,而不是一个函数,connect会自动在内部为你调用bindactioncreator

Because this is so common, connect supports an “object shorthand” form for the mapDispatchToProps argument: if you pass an object full of action creators instead of a function, connect will automatically call bindActionCreators for you internally.

我们建议总是使用mapDispatchToProps的“对象简写”形式,除非你有特定的理由来定制分派行为。

We recommend always using the “object shorthand” form of mapDispatchToProps, unless you have a specific reason to customize the dispatching behavior.

注意:

// React Redux does this for you automatically:
;(dispatch) => bindActionCreators(mapDispatchToProps, dispatch)

因此,我们的mapDispatchToProps可以简单地写成:

Therefore, our mapDispatchToProps can simply be:

const mapDispatchToProps = {
  increment,
  decrement,
  reset,
}

因为变量的实际名称由你决定,你可能想给它一个像actioncreator这样的名称,或者甚至在连接调用中内联定义对象:

Since the actual name of the variable is up to you, you might want to give it a name like actionCreators, or even define the object inline in the call to connect:

import { increment, decrement, reset } from './counterActions'

const actionCreators = {
  increment,
  decrement,
  reset,
}

export default connect(mapState, actionCreators)(Counter)

// or
export default connect(mapState, { increment, decrement, reset })(Counter)

常见问题

为什么我的组件不能接收分派?

也被称为

Also known as

// TypeError: this.props.dispatch is not a function

这是一个常见的错误,当你尝试调用this.props.dispatch,但dispatch没有注入到你的组件中。

This is a common error that happens when you try to call this.props.dispatch , but dispatch is not injected to your component.

只有在以下情况下,分派才会被注入到组件中:

dispatch is injected to your component only when:

  1. 你没有提供mapDispatchToProps
    1. You do not provide mapDispatchToProps

默认的mapDispatchToProps只是简单地dispatch => ({ dispatch })。如果您不提供mapDispatchToProps, dispatch将如上所述提供。

The default mapDispatchToProps is simply dispatch => ({ dispatch }). If you do not provide mapDispatchToProps, dispatch will be provided as mentioned above.

换句话说,如果你这样做:

In another words, if you do:

// component receives `dispatch`
connect(mapStateToProps /** no second argument*/)(Component)
  1. 您自定义的mapDispatchToProps函数的返回值特别包含dispatch
    1. Your customized mapDispatchToProps function return specifically contains dispatch

你可以通过提供自定义的mapDispatchToProps函数来恢复调度:

You may bring back dispatch by providing your customized mapDispatchToProps function:

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

或者,使用bindactioncreator:

Or alternatively, with bindActionCreators:

import { bindActionCreators } from 'redux'

function mapDispatchToProps(dispatch) {
  return {
    dispatch,
    ...bindActionCreators({ increment, decrement, reset }, dispatch),
  }
}

当你指定mapDispatchToProps时,是否要为你的组件提供分派(Dan Abramov对#255的回应)。为了进一步理解当前的实现意图,您可以阅读它们。

There are discussions regarding whether to provide dispatch to your components when you specify mapDispatchToProps ( Dan Abramov’s response to #255 ). You may read them for further understanding of the current implementation intention.

我可以mapDispatchToProps没有mapStateToProps在Redux?

是的。您可以通过传递undefinednull跳过第一个参数。您的组件不会订阅存储,仍然会接收mapDispatchToProps定义的分派道具。

Yes. You can skip the first parameter by passing undefined or null. Your component will not subscribe to the store, and will still receive the dispatch props defined by mapDispatchToProps.

connect(null, mapDispatchToProps)(MyComponent)

我可以调用store.dispatch吗?

React组件中直接与商店交互是一种反模式,无论是显式导入商店还是通过上下文访问它(有关商店设置的详细信息,请参阅Redux常见问题解答条目)。让React Reduxconnect处理对商店的访问,并使用它传递给道具的分派来分派动作。

It’s an anti-pattern to interact with the store directly in a React component, whether it’s an explicit import of the store or accessing it via context (see the Redux FAQ entry on store setup for more details). Let React Redux’s connect handle the access to the store, and use the dispatch it passes to the props to dispatch actions.