markdown Redux容器:mapDispatchToProps
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown Redux容器:mapDispatchToProps相关的知识,希望对你有一定的参考价值。
const filterProps = (props, onlyPropNames = []) => onlyPropNames.reduce((newProps, propName) => {
newProps[propName] = props[propName]
return newProps
}, {})
export const shouldFactoryBindProps = (prevProps, nextProps, comparePropNames = []) => {
if (prevProps === undefined || (prevProps !== undefined && nextProps === undefined)) { return true }
return comparePropNames.some(propName => prevProps[propName] !== nextProps[propName])
}
export const createDispatchPropsFactory = (createDispatchProps, comparePropNames = []) => {
let prevOwnProps
let dispatchProps
return () => {
return (dispatch, ownProps) => {
const shouldBind = shouldFactoryBindProps(prevOwnProps, ownProps, comparePropNames)
if (shouldBind) {
dispatchProps = createDispatchProps(dispatch, ownProps)
prevOwnProps = filterProps(ownProps, comparePropNames)
}
return dispatchProps
}
}
}
export const createActionDispatchers = (createDispatchProps, comparePropNames = []) => {
const argsLength = createDispatchProps.length
const bindOwnProps = argsLength === 2
const shouldCreateFactory = !!comparePropNames.length
if (bindOwnProps && shouldCreateFactory) {
return createDispatchPropsFactory(createDispatchProps, comparePropNames)
}
return createDispatchProps
}
export default createActionDispatchers
# Redux containers: `mapDispatchToProps`
This document details some tips and tricks for creating redux containers. Specifically, this document is looking at the `mapDispatchToProps` argument of the [`connect`][connect] function from [react-redux][react-redux]. There are many ways to write the same thing in redux. This gist covers the various forms that `mapDispatchToProps` can take.
[actions]: https://redux.js.org/docs/basics/Actions.html
[action-creators]: https://redux.js.org/docs/basics/Actions.html#action-creators
[bind-action-creators]: https://redux.js.org/docs/api/bindActionCreators.html
[connect]: https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options
[dispatch]: https://redux.js.org/docs/api/Store.html#dispatch
[events]: https://reactjs.org/docs/events.html
[lifecycle]: https://reactjs.org/docs/react-component.html#the-component-lifecycle
[react-redux]: https://github.com/reactjs/react-redux/
[react-redux-docs]: https://redux.js.org/docs/basics/UsageWithReact.html
[redux-thunk]: https://github.com/gaearon/redux-thunk
## Generic kitchen sink example
Before we dig too deep into how all of this works, it's a good idea to look at what we're trying to create. Here we can see an example of a `mapDispatchToProps` argument that uses *every feature*. This example highlights the key advantages of mixing the functional long-hand version of `mapDispatchToProps` with [`bindActionCreators`][bind-action-creators] and [thunks][redux-thunk].
**Enables:**
- access to `ownProps`
- access to `getState`
- controlling the `event`
- controlling dispatch
- conditional dispatches
- multiple dispatches
If you don't fully understand what these examples are doing, don't worry. We'll cover all of these pieces in great detail below. We're hoisting this to the top of this doc to make them accessible.
### With `bindActionCreators`
**Key idea:** Auto-dispatch a thunk.
Good for when you need access to `getState`. You don't normally need access to the redux state when you are dispatching. However, there are times when you need to know if something is (for instance) already fetching before trying to fetch it again.
Using `bindActionCreators` manually gives us access to both the short and long-hand syntaxes simultaneously.
```js
import { bindActionCreators } from 'redux'
import { honk, kill } from '../modules/goose/actions'
import { selectAlive } from '../modules/goose/selectors'
const mapDispatchToProps = (dispatch, ownProps) => bindActionCreators({
onClick: (event) => (_, getState) => {
event.preventDefault() // <-- control the event
const state = getState() // <-- access the state
const { id } = ownProps // <-- access props
const isAlive = selectAlive(state, id)
if (isAlive) { // <-- conditionally dispatch
dispatch(honk(id))
}
},
onClose: kill // <-- use short-hand if you want
}, dispatch)
```
**Note:** if you have no need to read from `ownProps` or `state`, you might prefer to use one of the simpler versions below.
### Without `bindActionCreators`
If you don't like the use of `bindActionCreators` above, you can accomplish the same thing using the long-hand version.
```js
import { honk, kill } from '../modules/goose/actions'
import { selectAlive } from '../modules/goose/selectors'
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: (event) => dispatch((_, getState) => { // <-- dispatch a thunk
event.preventDefault()
const state = getState()
const { id } = ownProps
const isAlive = selectAlive(state, id)
if (isAlive) {
dispatch(honk(id))
}
}),
onClose: (...args) => dispatch(kill(...args)) // <-- long-hand version of short-hand
})
```
### Simplest example
Of course, if you don't need access to `getState` or `ownProps`, you could get away with something more bare-bones. We'll see below how anything less than what's shown below starts to introduce some drawbacks.
```js
import { honk } from '../modules/goose/actions'
// without ownProps
const mapDispatchToProps = {
onClick: () => honk() // <-- rename to event; control the payload
}
// with ownProps
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: () => {
const { id } = ownProps
dispatch(honk(id)) // <-- control the dispatch
}
})
```
## What is `connect`?
You can read more about why `connect` exists in the [react-redux docs][react-redux-docs]. At a high level, you use `connect` to create redux containers. In practical terms, a redux container lets you hook the props of a react component to a redux store. If you are new to redux and are unsure what it does, you should start from [the beginning](https://redux.js.org/).
The [`connect`][connect] function accepts four arguments.
- *`mapStateToProps`* — selects values from the state; creates a `stateProps` object.
- *`mapDispatchToProps`* — dispatches actions; creates a `dispatchProps` object.
- *`mergeProps`* — *not commonly used.* Merges the property objects from `stateProps`, `dispatchProps` and `ownProps`.
- *`options`* — *not commonly used.* Options for deeper control over what how `connect` function operates.
**Key point:** this document is discussing the second argument, `mapDispatchToProps`.
## What is a container?
In effect, the `connect` function allows you to create redux containers — imagine if it were named `createContainer` instead. In a react-redux application, a container is a special type of component that has access to the redux store. The arguments passed to `connect` — `mapStateToProps` and `mapDispatchToProps` — are used to configure *how* the container communicate with the store. Both arguments are designed to gather props from the store and pass them to the child component.
A container is created by wrapping a child component in `connect`. In all of the examples below, imagine that our `mapDispatchToProps` argument will be creating props for a `<SomeButton />` component to use.
#### A quick note on `mapStateToProps`
The redux store has two key functions that are of interest: `getState` and `dispatch`. The first argument, `mapStateToProps`, is focused on the `getState` function. The point of the function is to return a `stateProps` object — essentially, a props object with values derived from the state.
#### A quick note on `mapDispatchToProps`
The second argument, `mapDispatchToProps` is focused on the `dispatch` function. The point is to generate a `dispatchProps` object. The result is a props object that contains *action dispatchers* — functions that automatically dispatch actions with the correct payload.
#### Example redux container:
Here you can see the container we'll be using in all of the examples below. Right now we're leaving both of `mapStateToProps` and `mapDispatchToProps` as `undefined`. We'll explore numerous examples of how to craft a `mapDispatchToProps` argument.
```js
import { connect } from 'react-redux'
import { honk } from '../modules/goose/actions' // <-- action creator
import SomeButton from './SomeButton'
const mapStateToProps = undefined
const mapDispatchToProps = undefined // <-- we're focusing on this one
// hook the props of SomeButton to the redux store
const SomeButtonContainer = connect( // <-- create a container
mapStateToProps,
mapDispatchToProps
)(SomeButton) // <-- child component
export default SomeButtonContainer
```
#### Example react component:
Here's what the `<SomeButton />` component looks like as well.
```js
import React from 'react'
import PropTypes from 'prop-types'
const SomeButton = ({ children, onClick }) => (
<button onClick={onClick}>
{children}
</button>
)
SomeButton.propTypes = {
children: PropTypes.node,
onClick: PropTypes.func
}
export default SomeButton
```
## What is an action creator?
You might enjoy reading more about [action creators][action-creators] in the redux manual.
Below we see the action creator that we will use for all of the examples below. For simplicity, we're showing the constant in the same file as our action creator. Typically, your action type constants should be kept in a separate location.
Imagine this file is located in a redux module at `src/modules/goose/actions/index.js`.
#### Example action creator
```js
export const GOOSE_HONK = 'GOOSE_HONK' // <-- action type
export const GOOSE_KILL = 'GOOSE_KILL'
export const honk = (payload) => ({ type: GOOSE_HONK, payload }) // <-- action creator
export const kill = (payload) => ({ type: GOOSE_KILL, payload })
```
**Note:** your *action type* constants should be kept in a separate file.
## What is `mapDispatchToProps`?
Specifically, `mapDispatchToProps` is the second argument that `connect` expects to receive. In the context of a react-redux application, the `mapDispatchToProps` argument is responsible for enabling a component to [dispatch][dispatch] [actions][actions]. In practical terms, `mapDispatchToProps` is where react [events][events] (and [lifecycle][lifecycle] events) are mapped to redux actions.
More specifically, `mapDispatchToProps` is where you should be dispatching most of your actions. The vast majority of actions in your react-redux application will originate from a `mapDispatchToProps` argument one way or the other. Any action originating from react, started in a `dispatchProps` object, created by a `mapDispatchToProps` argument.
These `dispatchProps` are all functions that dispatch actions. In the example react component above, the `onClick` function is assigned to an action dispatcher by the container.
The react-redux API docs for [`connect`][connect] mentions three different ways to specify the `mapDispatchToProps` argument. In all three forms, the point is to generate a `dispatchProps` object.
### Three versions of `mapDispatchToProps`
- **Object short-hand** — a key-value object of redux action creators. In the short-hand version, the actions are automatically dispatched using [`bindActionCreators`][bind-action-creators].
- **Functional long-hand** — a function that returns a key-value object of redux action creators. In the long-hand version, the actions are *not* auto-dispatched.
- **Factory function** — *not commonly used.* A factory function that returns a `mapDispatchToProps` function. Allows for manually memoizing the `dispatchProps` object.
**Key point:** The purpose of `mapDispatchToProps` is to create a `dispatchProps` object.
### What is a `dispatchProps` object?
Technically speaking, a `dispatchProps` object is a key-mapping of *action dispatchers*. More specifically, `dispatchProps` are merged into `ownProps` by a redux container and passed as merged props to the child component. Generally, the `dispatchProps` object is what allows a component to dispatch actions to a redux store.
- A `dispatchProps` object is a key-mapping of action dispatchers.
- An *action dispatcher* is a function that automatically dispatches one or more actions
- A [thunk][redux-thunk] would be considered an action dispatcher.
**Key point:** The functions in a `dispatchProps` object are *action dispatchers*.
### Standalone example of how `mapDispatchToProps` works
It may be helpful for to explore a toy example that demonstrates the core concepts of a `dispatchProps` object. The linked example shows how a `dispatchProps` object is used. Typically, a `dispatchProps` object is created and cached inside the container when `connect` is initialized.
**Play:** check out this standalone example: https://repl.it/@heygrady/dispatchProps
## Using `mapDispatchToProps`
As noted above, the `connect` function specifies three ways to define your `mapDispatchToProps` argument.
#### These three versions are functionally equivalent
```js
import { honk } from '../modules/goose/actions' // <-- action creator
// object short-hand version
const mapDispatchToProps = { onClick: honk } // <-- auto-dispatches
// functional long-hand version
const mapDispatchToProps = dispatch => ({
onClick: event => dispatch(honk(event)) // <-- manually dispatches
})
// avoid: factory version (usually unnecessary)
const mapDispatchToProps = () => {
let dispatchProps // <-- manually memoizes dispatchProps
return dispatch => {
if (!dispatchProps) { // <-- manually skips rebinding when ownProps changes
dispatchProps = {
onClick: event => dispatch(honk(event))
}
}
return dispatchProps
}
}
```
**Note:** the factory version is only useful when `ownProps` is specified (see below).
### Two functional long-hand versions
The `connect` function is heavily overloaded to enable many common-sense performance optimizations out of the box.
The most obvious examples of overloading `connect` are the three ways to specify the `mapDispatchToProps` argument: *short-hand*, *long-hand* and *factory*.
A less obvious optimization applies only to the *long-hand* form of `mapDispatchToProps`. The long-hand form is overloaded too! Under the hood, `connect` will check the argument length of your `mapDispatchToProps` function and handle it differently.
**If you specify only the `dispatch` argument**, the connect function will automatically memoize the result. It will only ever call your `mapDispatchToProps` function once! This has a great performance benefit in the case that your `dispatchProps` object is expensive to create. In any case, this memoization ensures that the long-hand version is just as performant as the short-hand object version.
**If you specify the optional second `ownProps` argument**, your `mapDispatchToProps` function will be called whenever props are updated. In cases where your `dispatchProps` object is expensive to create, this can be a minor performance issue. In very extreme cases you may benefit from the factory version if you need to use `ownProps`. However, you usually won't see any performance impact from including `ownProps`.
- **Ignore `ownProps` version** — only called when `connect` initializes
- **Bind `ownProps` version** — called whenever `ownProps` changes
Here we see the two overloaded forms for a `mapDispatchToProps` function.
```js
import { honk } from '../modules/goose/actions'
// ignore `ownProps`; binds only on init
const mapDispatchToProps = dispatch => ({
onClick: event => dispatch(honk()) // <-- empty payload
})
// bind `ownProps`; binds every time ownProps changes
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: event => dispatch(honk(ownProps.id)) // <-- bind id to payload
})
// avoid: wasteful; rebinds every time ownProps changes
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: event => dispatch(honk()) // <-- whoops! we're not using ownProps
})
```
**Key point:** you should not put `ownProps` in the signature of your `mapDispatchToProps` function unless you intend to use it.
## Why avoid the factory version?
It's important to keep in mind that the maintainers of react-redux have gone to great lengths to ensure good performance for most use cases. There are edge cases where you might want to do something *special* that `connect` doesn't handle out of the box. If you are using the factory version of `mapDispatchToProps` you are probably doing something *very* special.
Most of the time you would have no reason to manually memoize the `dispatchProps` object.
- If you use the short-hand version, `dispatchProps` is already memoized
- If you use the long-hand version, `dispatchProps` is *also* already memoized
- `mapDispatchToProps(dispatch)` will only be called once, when `connect` is initialized
- `mapDispatchToProps(dispatch, ownProps)` will be called whenever `ownProps` changes
- If you use the factory version, you will still be doing work to determine if you need to rebind your action creators
- Only useful when binding `ownProps`
- The inner `dispatchProps` creator will be called every time `ownProps` changes
Internally, `connect` determines if you're using the factory pattern based on what your `mapDispatchToProps` function returns when it is initialized. If you return a function instead of an object, it's assumed you're trying to specify a factory.
### Why does the factory version exist?
The only time that the factory version will yield performance benefits is in the case where `ownProps` updates frequently, yet only *specific* props are bound to your action creators. Say you are binding an `id` that never changes, but the `name` prop changes 60 times a second. In that case, you might be able to save some CPU cycles using the factory method.
To see benefits, your `mapDispatchToProps` factory:
- **must** access `ownProps`
- **must** have an expensive-to-create `dispatchProps` object
- **must** have noisy values in `ownProps` that are irrelevant to your `dispatchProps` object
**Note:** the factory version still does work every time `ownProps` changes. If your `mapDispatchToProps` isn't very complicated, there likely isn't any performance gain to using the factory version versus the functional version. However, if some unrelated values of `ownProps` are updating constantly (multiple times a second), the factory version can enable you to rebind to `ownProps` only when props you care about have changed.
Below you can see that the functional version, as opposed to the factory version, will rebind the `id` to `honk` on init *and* every time `ownProps` changes. If you were to change an unrelated prop, like `children`, the functional version would still rebind. By contrast, the factory version would only rebind the action creator if the `ownProps.id` were to change. Otherwise, changes to `ownProps` will not cause a rebind.
#### Example binding `ownProps` in a factory
```js
import { honk } from '../modules/goose/actions'
// functional version: might rebind too much
const mapDispatchToProps = (dispatch, ownProps) => { // <-- called on init and when ownProps changes
const { id } = ownProps
return {
onClick: event => dispatch(honk(id)) // <-- bind to id
}
}
// helper functions to manage cache and shallow compare
const filterProps = (props, comparePropNames = []) => comparePropNames.reduce((newProps, prop) => {
newProps[prop] = props[prop]
return newProps
}, {})
const shouldFactoryBindProps = (prevProps, nextProps, comparePropNames = []) => {
if (prevProps === undefined || (prevProps !== undefined && nextProps === undefined)) { return true }
if (prevProps === undefined && nextProps === undefined) { return false }
return comparePropNames.some(prop => prevProps[prop] !== nextProps[prop])
}
// factory version: rebinds only when absolutely necessary
const mapDispatchToPropsFactory = () => { // <-- only called on init
let prevOwnProps
let dispatchProps
const comparePropNames = ['id']
return (dispatch, ownProps) => { // <-- called on init and when ownProps changes
const shouldBind = shouldFactoryBindProps(prevOwnProps, ownProps, comparePropNames)
if (shouldBind) { // <-- skips rebinding
dispatchProps = mapDispatchToProps(dispatch, ownProps) // <-- notice, reusing mapDispatchToProps
prevOwnProps = filterProps(ownProps, comparePropNames)
}
return dispatchProps
}
}
}
```
**Note:** like the warnings about [pure components](https://hackernoon.com/react-purecomponent-considered-harmful-8155b5c1d4bc), it's important to notice that the work required to determine if we *should* rebind our action creators might not be any faster than simply rebinding.
**Play:** in the above example, we're actually wrapping our normal `mapDispatchToProps` function in a factory. If you want to see a generic factory creator, play with this example: https://repl.it/@heygrady/createActionDispatchers
## Why avoid the short-hand version; why prefer the long-hand version
While most developers are most familiar with the object short-hand version of the `mapDispatchToProps` argument, it should be avoided. The reasons are very subtle. If you are aware of the limitations of the short-hand version, feel free to use it. However, if you would like to write code that is easy to extend, consider the examples in this section.
#### These two versions are functionally equivalent
```js
import { honk } from '../modules/goose/actions'
// short-hand: notice that it dispatches a react event as the payload
const mapDispatchToProps = { onClick: honk }
// long-hand; this example is the functional equivalent of the short-hand version
const mapDispatchToProps = dispatch => ({
onClick: (event) => dispatch(honk(event)) // <-- whoops! passes react event as payload!
})
```
### Object short-hand version
The great benefit of the object short-hand version is that you can easily jam auto-dispatching actions into a component's props. This is most useful when initially sketching out functionality. Most developers prefer this format.
There are also a few downsides to the object short-hand version.
#### Pros
- Easy to write
- Easy to maximize performance
- Easy to dispatch thunks to access `dispatch` and `getState`
#### Cons
- Difficult to extend
- Pressure to offload work to components
- No control over payloads
- No access to `ownProps`
- Easy to accidentally dispatch a react event as the payload
- Many developers neglect to rename actions to make sense to a component
#### Object short-hand examples
```js
import { honk } from '../modules/goose/actions'
// prefer: rename the action, control payload
const mapDispatchToProps = {
onClick: () => honk() // <-- clear naming within component; ignore event; auto-dispatched
}
// avoid: passing action straight through to component
const mapDispatchToProps = {
honk // <-- unclear naming within the component; dispatches event
}
// avoid: letting the component control the payload
const mapDispatchToProps = {
onClick: honk // <-- dispatches react event as the payload
}
// avoid: auto-dispatching explicit return
const mapDispatchToProps = {
onClick: (event) => { // <-- control payload
event.preventDefault() // <-- manage events
return honk() // <-- awkward syntax; auto-dispatch
}
}
// prefer: use a thunk to manually dispatch
const mapDispatchToProps = {
onClick: (event) => (dispatch) => { // <-- access dispatch
event.preventDefault()
dispatch(honk()) // <-- manual dispatch
}
}
// prefer: use a thunk to getState
const mapDispatchToProps = {
onClick: (event) => (dispatch, getState) => {
event.preventDefault()
const state = getState() // <-- access state
const isAlive = selectAlive(state)
if (isAlive) { // <-- conditional dispatch
dispatch(honk(id))
}
}
}
```
### Functional long-hand version
The functional long-hand version gives you more control over how your container dispatches actions. While the syntax is slightly longer, you gain far more control. The long-hand version encourages best practices.
#### Pros
- Access to `dispatch`
- Access to `ownProps`
- Easy to extend
- Pressure to rename actions
- Pressure to control payloads
- Manually dispatch
#### Cons
- Hidden tricks can impact performance
#### Functional long-hand examples
```js
import { honk } from '../modules/goose/actions'
// prefer: ignore ownProps
const mapDispatchToProps = (dispatch) => ({ // ignore ownProps
onClick: (event) => {
event.preventDefault() // <-- manage events
dispatch(honk()) // <-- manually dispatch
}
})
// prefer: bind ownProps
const mapDispatchToProps = (dispatch, ownProps) => ({ // access ownProps
onClick: (event) => {
event.preventDefault()
const { id } = ownProps
dispatch(honk(id)) // <-- bind ownProps
}
})
// avoid: accessing ownProps without reason
const mapDispatchToProps = (dispatch, ownProps) => ({ // <-- wasteful
onClick: () => {
dispatch(honk()) // <-- not using ownProps
}
})
// avoid: passing ownProps from component
const mapDispatchToProps = (dispatch) => ({ // <-- ignore ownProps
onClick: (id) => { // <-- passes id from component
dispatch(honk(id))
}
})
// avoid: manually dispatching a thunk
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: (event) => {
event.preventDefault()
const { id } = ownProps
dispatch((_, getState) => { // <-- awkward; access getState
const state = getState()
const isAlive = selectAlive(state)
if (isAlive) {
dispatch(honk(id))
}
})
}
})
// prefer: auto-dispatching a thunk
const mapDispatchToProps = (dispatch, ownProps) => bindActionCreators({
onClick: (event) => (_, getState) => {
event.preventDefault()
const { id } = ownProps
const state = getState()
const isAlive = selectAlive(state)
if (isAlive) {
dispatch(honk(id))
}
}
}, dispatch)
```
### Using `bindActionCreators` with the functional long-hand version
When you need access to the state, you will benefit from the way that `bindActionCreators` auto-dispatches your action creators. You can gain access to `getState` by returning a thunk.
#### Pros
- Access to `getState`
- Conditionally dispatch based on current state
- Combines the benefits of the short-hand and long-hand versions
#### Cons
- Potentially create unnecessary thunks
- Accidentally dispatching twice
- Accidentally dispatching undefined
#### Functional long-hand (with `bindActionCreators`) examples
```js
import { honk, kill } from '../modules/goose/actions'
import { selectAlive } from '../modules/goose/selectors'
// prefer: access getState
const mapDispatchToProps = dispatch => bindActionCreators({ // <-- ignore ownProps
onClick: (event) => (_, getState) => { // <-- ignore inner dispatch; access getState
const state = getState()
const isAlive = selectAlive(state)
if (isAlive) { // <-- conditional dispatch
dispatch(honk())
}
}
}, dispatch)
// prefer: ignore getState
const mapDispatchToProps = (dispatch, ownProps) => bindActionCreators({ // <-- access ownProps
onClick: () => () => { // <-- ignore getState
const { id } = ownProps
dispatch(honk(id)) // <-- no explicit or implicit return
}
}, dispatch)
// prefer: mix short-hand and long-hand versions
const mapDispatchToProps = dispatch => bindActionCreators({
onClick: () => () => { // <-- long-hand; control payload
dispatch(honk())
},
onClose: kill // <-- short-hand; uncontrolled payload
}, dispatch)
// avoid: double-dispatch
const mapDispatchToProps = dispatch => bindActionCreators({
onClick: () => dispatch(honk()) // <-- whoops! dispatches the action twice
}, dispatch)
// avoid: awkward explicit return dispatch
const mapDispatchToProps = dispatch => bindActionCreators({
onClick: () => {
return honk() // <-- awkward, returned value is dispatched
}
}, dispatch)
// avoid: accessing ownProps without reason
const mapDispatchToProps = (dispatch, ownProps) => bindActionCreators({ // <-- wasteful
onClick: () => () => {
dispatch(honk())
}
}, dispatch)
// avoid: reassigning dispatch
const mapDispatchToProps = dispatch => bindActionCreators({
onClick: () => (dispatch, getState) => { // <-- avoid: reassigns dispatch
const state = getState()
const isAlive = selectAlive(state)
if (isAlive) {
dispatch(honk())
}
}
}, dispatch)
```
### A quick note on auto-dispatching
Using `bindActionCreators` auto-dispatches your actions, which enables short-hand mapping of actions to props. If you don't care what your payload is or you prefer to set your payloads in your components, bound actions can be very convenient. Because of this, developers are probably more familiar with the short-hand notation.
Below we can see an example of both the short-hand and the long-hand versions. The `dispatchProps` object is expected to manage the dispatching of actions itself. The short-hand notation uses `bindActionCreators` to automatically bind all of your action creators while the long-hand version leaves that step up to the developer.
A careless developer may not notice the mistake below. In this example, nothing would ever be dispatched.
```js
import { honk } from '../modules/goose/actions'
// works as expected
const mapDispatchToProps = {
onClick: honk // <-- yay: auto dispatches
}
// doesn't auto-dispatch
const mapDispatchToProps = dispatch => ({
onClick: honk // <-- whoops! doesn't dispatch
})
```
---
## Best practices: where to bind props?
At the top of this document we mention that a container wraps a component. Here we briefly explore what a react-redux application looks like from the perspective of a component.
### Prefer: binding props inside the container
Here we're showing the preferred example where our container reads useful values from `ownProps` and keeps the component in the dark about the id. For completeness, we're using prop-types to ensure that our ID
#### Good container:
```js
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { honk } from '../modules/goose/actions'
import SomeButton from './SomeButton'
// prefer: bind ownProps
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: () => {
const { id } = ownProps
dispatch(honk(id)) // <-- bind the payload
}
})
const SomeButtonContainer = connect(
undefined,
mapDispatchToProps
)(SomeButton)
SomeButtonContainer.propTypes = {
id: PropTypes.string.isRequired // <-- prefer: type-check your container props
}
export default SomeButtonContainer
```
#### Good component:
```js
import React from 'react'
import PropTypes from 'prop-types'
const SomeButton = ({ children, onClick }) => {
return (
<button onClick={onClick}>
{children}
</button>
)
}
SomeButton.propTypes = {
children: PropTypes.node,
onClick: PropTypes.func
}
export default SomeButton
```
### Avoid: binding props inside the component
Here, we're moving the burden for binding props to the component. Now the component needs to pull in props it might not otherwise care about. Additionally, the component needs to rebind the id to the `onClick` function on every render. This isn't terribly expensive but it's work a component shouldn't be doing.
#### Bad container:
```js
import { connect } from 'react-redux'
import { honk } from '../modules/goose/actions'
import SomeButton from './SomeButton'
// avoid: payload managed by component
const mapDispatchToProps = {
onClick: honk // <-- no control of payload
}
const SomeButtonContainer = connect(
undefined,
mapDispatchToProps
)(SomeButton)
export default SomeButtonContainer
```
#### Bad component:
```js
import React from 'react'
import PropTypes from 'prop-types'
const SomeButton = ({ children, id, onClick }) => {
const boundOnClick = () => onClick(id) // <-- avoid: re-binds id when props change
return (
<button onClick={boundOnClick}>
{children}
</button>
)
}
SomeButton.propTypes = {
children: PropTypes.node,
id: PropTypes.string, // <-- extra prop
onClick: PropTypes.func
}
export default SomeButton
```
以上是关于markdown Redux容器:mapDispatchToProps的主要内容,如果未能解决你的问题,请参考以下文章