什么是mapDispatchToProps?

425

我正在阅读Redux库的文档,其中有以下示例:

除了读取状态外,容器组件还可以派发操作。以类似的方式,您可以定义一个名为mapDispatchToProps()的函数,该函数接收dispatch()方法并返回您想要注入到表示组件中的回调属性。

实际上这没有意义。既然已经有了mapStateToProps,为什么需要mapDispatchToProps呢?

他们也提供了以下便捷的代码示例:

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

这个函数是什么,为什么它很有用?

6个回答

651

我觉得没有一个答案能够阐明为什么mapDispatchToProps很有用。

这个问题只有在容器组件-展示组件模式的背景下才能回答,最好先阅读 容器组件 然后是与React一起使用

简而言之,你的组件只应该关心展示内容, 唯一从哪里获取信息的地方是它们的props。

与“显示内容”(组件)分开的是:

  • 如何获取要显示的内容,
  • 以及如何处理事件。

这就是容器的作用。

因此,在该模式下,“设计良好”的组件应该像这样:

class FancyAlerter extends Component {
    sendAlert = () => {
        this.props.sendTheAlert()
    }

    render() {
        <div>
          <h1>Today's Fancy Alert is {this.props.fancyInfo}</h1>
          <Button onClick={sendAlert}/>
        </div>
     }
}

查看此组件如何从 props 中获取信息(这些信息通过 mapStateToProps 从 Redux store 中获取),并从其 props 中获取其操作函数:sendTheAlert()

这就是 mapDispatchToProps 的作用:在相应的 container 中实现。

// FancyButtonContainer.js

function mapDispatchToProps(dispatch) {
    return({
        sendTheAlert: () => {dispatch(ALERT_ACTION)}
    })
}

function mapStateToProps(state) {
    return({fancyInfo: "Fancy this:" + state.currentFunnyString})
}

export const FancyButtonContainer = connect(
    mapStateToProps, mapDispatchToProps)(
    FancyAlerter
)

我想知道你是否能够看到,现在它是container1知道redux,dispatch,store和state等内容。

模式中的FancyAlerter组件负责渲染,不需要了解这些内容:它通过props获得调用方法,用于按钮的onClick事件。

mapDispatchToProps是redux提供的有用手段,让容器可以轻松地将该函数传递到包装的组件上的props中。

所有这些看起来都很像文档中的todo示例和另一个答案,但我试图从模式的角度解释它,以强调为什么要这样做。

(请注意:mapStateToProps不能用于与mapDispatchToProps相同的目的,其基本原因是您无法在mapStateToProps内部访问dispatch。因此,您不能使用mapStateToProps为包装的组件提供使用dispatch的方法。)

我不知道为什么他们选择将其分成两个映射函数 - 可能更整洁的方法是mapToProps(state, dispatch, props),即一个函数同时完成两个功能!


1请注意,我故意明确地将容器命名为FancyButtonContainer,以突出它是一个“东西” - 容器的身份(因此也存在!)有时会在缩写中丢失

export default connect(...)

这是大多数示例中显示的语法缩写


8
我仍然想知道:为什么在组件内直接调用store.dispatch无法处理所需的功能? - pixelpax
12
@user2130130 没有任何特定内容。它关于良好的设计。如果您将组件与store.dispatch耦合在一起,那么当您决定分离redux或想要在不基于redux的其他地方使用它时(或者其他我现在想不到的东西),您将面临许多更改。您的问题概括为“为什么我们要遵循‘良好的设计实践’-无论您如何编写代码,都可以获得相同的功能”。mapDispatchToProps提供了清晰解耦的组件的设计方法。 - GreenAsJade
5
我不明白的是:ALERT_ACTION 到底是指代操作函数还是从操作函数返回的 type 类型?:/ 感到很困惑。 - Jamie Hutber
2
如果您有多个需要作为属性传递给子组件的操作创建器,其中一个替代方案是在mapDispatchToProps中使用bindActionCreators进行包装(请参阅http://redux.js.org/docs/api/bindActionCreators.html);另一种选择是仅提供一个操作创建器对象以连接(),例如connect(mapStateToProps,{actioncreators}),react-redux将为您包装它们并使用dispatch()。 - dave
1
谢谢您的解释,但还有几个问题:1)如果您将组件与store.dispatch耦合在一起,那么当您决定将redux分离出来,或者想要在不基于redux的地方使用它时[...]您将被困在许多更改中=>如果要从容器中重构redux,那么比从组件中重构更容易吗?在任何情况下,都只是将操作更改为其他范例,对吧?2)这些容器应该存在于哪个级别?每个组件都有一个容器吗?您的示例有点表明了这一点,也许这只是一个简化?非常感谢。 - 1252748
显示剩余11条评论

100

这基本上是一种速记法,所以不用写:

this.props.dispatch(toggleTodo(id));

您可以像示例代码中展示的那样使用mapDispatchToProps,然后在其他地方编写:

this.props.onTodoClick(id);

或者更可能的情况是,您将其作为事件处理程序:

<MyComponent onClick={this.props.onTodoClick} />

这里有一段由Dan Abramov提供的有用视频:Redux: Generating Containers with connect() from React Redux (VisibleTodoList)


2
谢谢。我也想知道,dispatch 最初是如何添加到 props 中的? - Code Whisperer
17
如果您不提供自己的mapDispatch函数,Redux将使用默认函数。该默认mapDispatch函数只是获取dispatch函数引用,并将其作为this.props.dispatch提供给您。 - markerikson
10
派发方法由提供者组件传递下来。 - Diego Haz

67
mapStateToProps()是一个实用工具,帮助您的组件获取更新的状态(由其他组件更新),
mapDispatchToProps()是一个实用工具,可以帮助您的组件触发一个操作事件(派发可能会导致应用程序状态改变的操作)。

28
react-redux 库中的 mapStateToPropsmapDispatchToPropsconnect 提供了一种方便的方式来访问你的存储器的 statedispatch 函数。因此,connect 实际上是一个高阶组件,您也可以将其视为包装器(如果这对您有意义的话)。每当您的 state 更改时,mapStateToProps 将使用新的 state 调用,随后您的 props 更新组件会运行渲染函数以在浏览器中呈现您的组件。mapDispatchToProps 也会在组件的 props 上存储键值对,通常它们采用函数的形式。以这种方式,您可以从组件的 onClickonChange 事件触发状态更改。 来自文档的说明:
const TodoListComponent = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

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

function toggleTodo(index) {
  return { type: TOGGLE_TODO, index }
}

const TodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList) 

同时确保您熟悉 React 无状态组件高阶组件


调度器就像事件一样,基本上是这样吗? - Code Whisperer
2
这可能与事件有关,dispatch只是一个函数,也是改变应用程序state的唯一方式。mapStateToProps是将你的storedispatch功能暴露给React组件的一种方式。同时需要注意的是,connect并不是redux的一部分,它仅仅是一个实用工具和样板减少库,称为react-redux,用于在React和Redux之间进行交互。如果你将你的store从根React组件传递到子组件中,你也可以达到相同的结果,而无需使用react-redux - Vlad Filimon

7
mapStateToProps 方法接收 stateprops 两个参数,允许你从 state 中提取 props 并将其传递给组件。 mapDispatchToProps 方法接收 dispatchprops 两个参数,用于将 action creator 绑定到 dispatch 上,所以当你执行生成的函数时,action 将会被派发。 我觉得这样做只是省去了在组件内部使用 dispatch(actionCreator()) 的过程,使代码更容易阅读。详情请参考:React redux: connect: Arguments

谢谢,但dispatch方法的价值是什么?它来自哪里? - Code Whisperer
哦。调度基本上使动作启动redux / flux单向流程。其他答案似乎比这个更好地回答了您的问题。 - Harry Moreno
此外,dispatch方法来自于<Provider/>本身提供的增强功能。它通过自己的高阶组件魔法来保证dispatch在其子组件中可用。 - Ricardo Magalhães

6

现在假设Redux中有一个操作:

export function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

当你执行导入操作时,

import {addTodo} from './actions';

class Greeting extends React.Component {

    handleOnClick = () => {
        this.props.onTodoClick(); // This prop acts as key to callback prop for mapDispatchToProps
    }

    render() {
        return <button onClick={this.handleOnClick}>Hello Redux</button>;
    }
}

const mapDispatchToProps = dispatch => {
    return {
      onTodoClick: () => { // handles onTodoClick prop's call here
        dispatch(addTodo())
      }
    }
}

export default connect(
    null,
    mapDispatchToProps
)(Greeting);

如函数名所示mapDispatchToProps(),它将dispatch操作映射到属性(我们组件的属性)

因此,属性onTodoClick是委托给分派操作addTodomapDispatchToProps函数的关键。

如果您想要简化代码并避免手动实现,则可以这样做,

import {addTodo} from './actions';
class Greeting extends React.Component {

    handleOnClick = () => {
        this.props.addTodo();
    }

    render() {
        return <button onClick={this.handleOnClick}>Hello Redux</button>;
    }
}

export default connect(
    null,
    {addTodo}
)(Greeting);

这意味着什么

const mapDispatchToProps = dispatch => {
    return {
      addTodo: () => { 
        dispatch(addTodo())
      }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,