Skip to main content

连接:使用 mapDispatchToProps 调度操作

¥Connect: Dispatching Actions with mapDispatchToProps

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

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

dispatch 是 Redux 存储的一个函数。你调用 store.dispatch 来调度操作。这是触发状态更改的唯一方法。

¥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 为你提供了两种让组件分派操作的方法:

¥With React Redux, your components never access the store directly - connect does it for you. React Redux gives you two ways to let components dispatch actions:

  • 默认情况下,连接的组件接收 props.dispatch 并可以自行调度操作。

    ¥By default, a connected component receives props.dispatch and can dispatch actions itself.

  • connect 可以接受名为 mapDispatchToProps 的参数,它允许你创建在调用时分派的函数,并将这些函数作为 props 传递给你的组件。

    ¥connect can accept an argument called mapDispatchToProps, which lets you create functions that dispatch when called, and pass those functions as props to your component.

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

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

调度方式

¥Approaches for Dispatching

默认:dispatch 作为属性

¥Default: dispatch as a Prop

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

¥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。你可以使用它向存储发送操作。

¥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 参数

¥Providing A mapDispatchToProps Parameter

提供 mapDispatchToProps 允许你指定组件可能需要分派哪些操作。它允许你提供动作调度函数作为 props。因此,你可以直接调用 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.

更具声明性

¥More Declarative

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

¥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.

一个很好的例子是单击按钮时调度一个操作。直接连接按钮在概念上可能没有意义,并且让按钮引用 dispatch 也没有意义。

¥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} />

一旦你使用调度操作的函数封装了我们所有的操作创建器,该组件就不再需要 dispatch。因此,如果你定义自己的 mapDispatchToProps,则连接的组件将不再接收 dispatch

¥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.

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

¥Pass Down Action Dispatching Logic to ( Unconnected ) Child Components

此外,你还能够将操作调度函数传递给子组件(可能未连接)。这允许更多组件分派操作,同时保持 Redux 的 "unaware"。

¥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 Redux 的 connect 所做的 - 它封装了与 Redux 存储对话的逻辑,让你不用担心它。这就是你在实现中应该充分利用的。

¥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 的两种形式

¥Two Forms of mapDispatchToProps

mapDispatchToProps 参数可以有两种形式。函数形式允许更多定制,而对象形式则易于使用。

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

  • 函数形式:允许更多定制,可以访问 dispatch 和可选的 ownProps

    ¥Function form: Allows more customization, gains access to dispatch and optionally ownProps

  • 对象简写形式:更具声明性且更易于使用

    ¥Object shorthand form: More declarative and easier to use

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

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

mapDispatchToProps 定义为函数

¥Defining mapDispatchToProps As A Function

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(可选)

    ¥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' }),
}
}

你可能还想将参数转发给你的动作创建者:

¥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( 可选的 )

¥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.

这意味着,你可以在组件的 props 更改时这样做,而不是在组件重新渲染时将新的 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.

绑定在组件安装上

¥Binds on component mount

render() {
return <button onClick={() => this.props.toggleTodo(this.props.todoId)} />
}

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

绑定 props 更改

¥Binds on props change

render() {
return <button onClick={() => this.props.toggleTodo()} />
}

const mapDispatchToProps = (dispatch, ownProps) => {
return {
toggleTodo: () => dispatch(toggleTodo(ownProps.todoId))
}
}

返回

¥Return

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

¥Your mapDispatchToProps function should return a plain object:

  • 对象中的每个字段将成为你自己的组件的单独属性,并且该值通常应该是在调用时调度操作的函数。

    ¥Each field in the object will become a separate prop for your own component, and the value should normally be a function that dispatches an action when called.

  • 如果你在 dispatch 中使用操作创建者(与普通对象操作相反),则按照约定简单地将字段键命名为与操作创建者相同的名称:

    ¥If you use action creators ( as oppose to plain object actions ) inside dispatch, it is a convention to simply name the field key the same name as the action creator:

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

(计数器示例的完整代码是 在这个 CodeSandbox 中

¥(Full code of the Counter example is in this CodeSandbox)

使用 bindActionCreators 定义 mapDispatchToProps 函数

¥Defining the mapDispatchToProps Function with bindActionCreators

手动封装这些函数非常乏味,因此 Redux 提供了一个函数来简化这一过程。

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

bindActionCreators 将值为 动作创造者 的对象转换为具有相同键的对象,但每个操作创建者都封装到 dispatch 调用中,以便可以直接调用它们。见 bindActionCreators 上的 Redux 文档

¥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

bindActionCreators 接受两个参数:

¥bindActionCreators accepts two parameters:

  1. function(动作创建者)或 object(每个字段一个动作创建者)

    ¥A function (an action creator) or an object (each field an action creator)

  2. dispatch

bindActionCreators 生成的封装函数将自动转发其所有参数,因此你无需手动执行此操作。

¥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 函数中使用 bindActionCreators

¥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

¥Manually Injecting dispatch

如果提供了 mapDispatchToProps 参数,组件将不再接收默认的 dispatch。你可以通过手动将其添加到 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 定义为对象

¥Defining mapDispatchToProps As An Object

你已经看到,在 React 组件中分派 Redux 操作的设置遵循非常相似的过程:定义一个动作创建器,将其封装在另一个类似于 (…args) => dispatch(actionCreator(…args)) 的函数中,并将该封装函数作为 prop 传递给你的组件。

¥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 将在内部自动为你调用 bindActionCreators

¥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.

注意:

¥Note that:

  • mapDispatchToProps 对象的每个字段都被假定为一个动作创建者

    ¥Each field of the mapDispatchToProps object is assumed to be an action creator

  • 你的组件将不再接收 dispatch 作为 prop

    ¥Your component will no longer receive dispatch as a prop

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

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

¥Therefore, our mapDispatchToProps can simply be:

const mapDispatchToProps = {
increment,
decrement,
reset,
}

由于变量的实际名称取决于你,你可能需要给它一个像 actionCreators 这样的名称,甚至在对 connect 的调用中内联定义对象:

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

常见问题

¥Common Problems

为什么我的组件没有收到 dispatch

¥Why is my component not receiving dispatch?

也称为

¥Also known as

TypeError: this.props.dispatch is not a function

这是当你尝试调用 this.props.dispatchdispatch 未注入到你的组件时发生的常见错误。

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

仅当出现以下情况时,dispatch 才会注入到你的组件中:

¥dispatch is injected to your component only when:

1.

默认的 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)

2.

你可以通过提供自定义的 mapDispatchToProps 函数来带回 dispatch

¥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,
}
}

或者,使用 bindActionCreators

¥Or alternatively, with bindActionCreators:

import { bindActionCreators } from 'redux'

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

参见 Redux 的 GitHub 问题 #255 中出现此错误

¥See this error in action in Redux’s GitHub issue #255.

关于当你指定 mapDispatchToProps ( 丹·阿布拉莫夫 (Dan Abramov) 对 #255 的回应 ) 时是否向组件提供 dispatch 存在讨论。你可以阅读它们以进一步了解当前的实现意图。

¥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.

我可以在 Redux 中 mapDispatchToProps 而不使用 mapStateToProps 吗?

¥Can I mapDispatchToProps without mapStateToProps in 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 吗?

¥Can I call store.dispatch?

在 React 组件中直接与存储交互是一种反模式,无论是显式导入存储还是通过上下文访问它(有关更多详细信息,请参阅 Redux 存储设置常见问题解答条目)。让 React Redux 的 connect 处理对 store 的访问,并使用它传递给 props 的 dispatch 来调度操作。

¥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.

¥Links and References

教程

¥Tutorials

相关文档

¥Related Docs

Q&A