连接:使用 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 calledmapDispatchToProps
, 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 optionallyownProps
对象简写形式:更具声明性且更易于使用
¥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
定义为函数可以让你最灵活地自定义组件接收的函数以及它们如何调度操作。你可以访问 dispatch
和 ownProps
。你可以利用这个机会编写由连接的组件调用的自定义函数。
¥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
dispatch
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 adispatch
call so they may be invoked directly. See Redux Docs onbindActionCreators
bindActionCreators
接受两个参数:
¥bindActionCreators
accepts two parameters:
function
(动作创建者)或object
(每个字段一个动作创建者)¥A
function
(an action creator) or anobject
(each field an action creator)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.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
才会注入到你的组件中:
¥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?
是的。你可以通过传递 undefined
或 null
来跳过第一个参数。你的组件不会订阅存储,但仍会收到 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
如何使用 Redux 连接从
this.props
获得简单的调度?¥How to get simple dispatch from
this.props
using connect with Redux?this.props.dispatch
isundefined
if usingmapDispatchToProps
不调用
store.dispatch
,而是调用connect
注入的this.props.dispatch
¥Do not call
store.dispatch
, callthis.props.dispatch
injected byconnect
instead