mapDispatch: options for dispatching actions with connect">mapDispatch: options for dispatching actions with connect">
跳至主要内容

连接:使用 mapDispatchToProps 派发操作

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

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

使用 React Redux,您的组件永远不会直接访问存储 - connect 会为您完成。React Redux 提供两种方法让组件派发操作

  • 默认情况下,连接的组件会收到 props.dispatch 并可以自行派发操作。
  • connect 可以接受一个名为 mapDispatchToProps 的参数,它允许您创建在调用时派发的函数,并将这些函数作为 props 传递给您的组件。

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

分发方法

默认:dispatch 作为道具

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

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

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

以这种方式连接组件后,您的组件将接收 props.dispatch。您可以使用它将操作分派到存储。

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())。您可能想要这样做有几个原因。

更具声明性

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

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

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

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

将所有操作创建者包装在分派操作的函数中后,组件就不再需要 dispatch。因此,如果您定义了自己的 mapDispatchToProps,连接的组件将不再接收 dispatch

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

此外,您还可以将动作分发函数传递给子组件(可能没有连接)。这允许更多组件分发动作,同时保持它们对 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 store 交互的逻辑,让您不必担心它。这正是您在实现中应该充分利用的。

mapDispatchToProps 的两种形式

mapDispatchToProps 参数可以有两种形式。虽然函数形式允许更多自定义,但对象形式易于使用。

  • 函数形式:允许更多自定义,可以访问 dispatch 和可选的 ownProps
  • 对象简写形式:更具声明性,更易于使用

注意:除非您需要以某种方式自定义分发行为,否则我们建议使用 mapDispatchToProps 的对象形式。

mapDispatchToProps 定义为函数

mapDispatchToProps 定义为函数可以使您在自定义组件接收的函数以及它们如何分发动作方面具有最大的灵活性。您可以访问 dispatchownProps。您可以利用此机会编写自定义函数供连接的组件调用。

参数

  1. dispatch
  2. ownProps(可选)

dispatch

mapDispatchToProps 函数将使用 dispatch 作为第一个参数调用。您通常会通过返回新的函数来利用它,这些函数在自身内部调用 dispatch(),并将一个简单的动作对象直接传递进去,或者传递动作创建者的结果。

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

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

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

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

ownProps(可选)

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

这意味着,您可以根据组件的 props 更改来进行操作,而不是在组件重新渲染时将新的 props 重新绑定到动作分发器。

在组件挂载时绑定

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

返回

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

  • 对象中的每个字段将成为你自己的组件的单独属性,并且该值通常应该是一个在调用时分派操作的函数。
  • 如果你在 dispatch 中使用操作创建者(而不是普通对象操作),则约定将字段键命名为与操作创建者相同的名称
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 函数的返回值将合并到你的连接组件中作为属性。你可以直接调用它们来分派其操作。

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 中

使用 bindActionCreators 定义 mapDispatchToProps 函数

手动包装这些函数很繁琐,因此 Redux 提供了一个函数来简化它。

bindActionCreators 将一个其值为 操作创建者 的对象转换为具有相同键的对象,但每个操作创建者都包装在一个 dispatch 调用中,以便它们可以直接调用。参见 Redux 文档关于 bindActionCreators

bindActionCreators 接受两个参数

  1. 一个 function(操作创建者)或一个 object(每个字段都是一个操作创建者)
  2. dispatch

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

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

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 参数,则组件将不再接收默认的 dispatch。你可以通过将其手动添加到 mapDispatchToProps 的返回值中来将其带回来,尽管大多数情况下你不需要这样做

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

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

mapDispatchToProps 定义为对象

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

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

我们建议始终使用 mapDispatchToProps 的“对象简写”形式,除非你有特定原因需要自定义分派行为。

注意

  • mapDispatchToProps 对象的每个字段都被假定为操作创建者
  • 您的组件将不再接收dispatch作为道具
// React Redux does this for you automatically:
;(dispatch) => bindActionCreators(mapDispatchToProps, dispatch)

因此,我们的mapDispatchToProps可以简化为

const mapDispatchToProps = {
increment,
decrement,
reset,
}

由于变量的实际名称由您决定,您可能希望将其命名为actionCreators,甚至可以在调用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)

常见问题

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

也称为

TypeError: this.props.dispatch is not a function

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

dispatch仅在以下情况下注入到您的组件中

1. 您没有提供mapDispatchToProps

默认的mapDispatchToProps只是dispatch => ({ dispatch })。如果您没有提供mapDispatchToPropsdispatch将按上述方式提供。

换句话说,如果您做

// component receives `dispatch`
connect(mapStateToProps /** no second argument*/)(Component)

2. 您自定义的mapDispatchToProps函数返回的具体内容包含dispatch

您可以通过提供自定义的mapDispatchToProps函数来恢复dispatch

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

或者,使用bindActionCreators

import { bindActionCreators } from 'redux'

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

查看 Redux 的 GitHub 问题 #255 中的此错误示例

关于在您指定mapDispatchToProps时是否向您的组件提供dispatch,存在一些讨论(Dan Abramov 对 #255 的回复)。您可以阅读它们以进一步了解当前实现的意图。

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

可以。您可以通过传递undefinednull来跳过第一个参数。您的组件不会订阅商店,但仍将接收由mapDispatchToProps定义的调度道具。

connect(null, mapDispatchToProps)(MyComponent)

我可以调用store.dispatch吗?

在 React 组件中直接与 store 交互是一种反模式,无论是以显式导入 store 还是通过 context 访问它(有关更多详细信息,请参阅Redux FAQ 中关于 store 设置的条目)。让 React Redux 的 connect 处理对 store 的访问,并使用它传递给 props 的 dispatch 来调度 actions。

教程

相关文档

问答