什么时候在 react/redux 中使用 bindActionCreators?

IT技术 reactjs redux react-redux
2021-04-13 15:58:18

bindActionCreators 的Redux文档指出:

唯一的用例bindActionCreators是当您想要将一些动作创建者传递给不知道 Redux 的组件,并且您不想将 dispatch 或 Redux 存储传递给它时。

什么bindActionCreators是使用/需要的示例

哪种组件不知道Redux

两种选择的优缺点是什么?

//actionCreator
import * as actionCreators from './actionCreators'

function mapStateToProps(state) {
  return {
    posts: state.posts,
    comments: state.comments
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(actionCreators, dispatch)
}

对比

function mapStateToProps(state) {
  return {
    posts: state.posts,
    comments: state.comments
  }
}

function mapDispatchToProps(dispatch) {
  return {
    someCallback: (postId, index) => {
      dispatch({
        type: 'REMOVE_COMMENT',
        postId,
        index
      })
    }
  }
}
6个回答

我不认为最受欢迎的答案实际上解决了这个问题。

下面的所有示例基本上都做同样的事情,并遵循无“预绑定”的概念。

// option 1
const mapDispatchToProps = (dispatch) => ({
  action: () => dispatch(action())
})


// option 2
const mapDispatchToProps = (dispatch) => ({
  action: bindActionCreators(action, dispatch)
})


// option 3
const mapDispatchToProps = {
  action: action
}

Option#3只是 option 的简写#1,所以真正的问题是为什么要使用 option #1vs option #2我已经看到它们都用于 react-redux 代码库,我发现它相当混乱。

我认为混淆来自这样一个事实,即doc中的所有示例react-redux使用,bindActionCreatorsbindActionCreators的 doc (如问题本身所引用的)说不要将它与 react-redux 一起使用。

我想答案是代码库的一致性,但我个人更喜欢在需要时dispatch 中显式包装操作

我一直在寻找这个答案。谢谢。
2021-05-22 15:58:18
option 如何是 option#3的简写#1
2021-06-06 15:58:18
我实际上遇到了 React 文档现在谈论的特定用例“将一些动作创建者传递给不知道 Redux 的组件”,因为我有一个简单的组件连接到一个更复杂的组件并增加了开销如果我可以只传递一个道具,那么简单组件reduxconnectaddDispatchToProps似乎有点过分。但据我所知,几乎所有mapDispatchto props 的情况要么是选项,#1要么#3在答案中提到
2021-06-10 15:58:18
@ArtemBernatskyi 谢谢。所以,原来有3种情况为mapDispatchToPropsfunctionobject和失踪。此处定义如何处理每种情况
2021-06-13 15:58:18

99% 的情况下,它与 React-Reduxconnect()函数一起使用,作为mapDispatchToProps参数的一部分它可以在mapDispatch您提供函数中显式使用,或者如果您使用对象速记语法并将一个充满动作创建者的对象传递给connect.

这个想法是,通过预先绑定动作创建者,您传递给connect()技术上的组件“不知道”它已连接 - 它只知道它需要运行this.props.someCallback()另一方面,如果您没有绑定动作创建者并调用this.props.dispatch(someActionCreator()),那么现在组件“知道”它已连接,因为它期望props.dispatch存在。

我在我的博客文章Idiomatic Redux:Why use action creators 中写了一些关于这个主题的想法.

但它与“mapDispatchToProps”相关联,这很好。似乎绑定动作创建者实际上是一件消极/毫无意义的事情,因为在调试等许多其他事情中,您将丢失函数定义(TS 或 Flow)。在我的新项目中,我从不使用它,迄今为止我没有遇到任何问题。还使用 Saga 和持久状态。此外,如果您只是调用 redux 函数(尽管点击效果不错),我会说这比“附加”动作创建器更简洁,在我看来,这是杂乱无章的。您的智能(通常是屏幕组件)仍然可以使用连接,但仅用于道具。
2021-05-29 15:58:18

更完整的例子,传递一个充满动作创建者的对象来连接:

import * as ProductActions from './ProductActions';

// component part
export function Product({ name, description }) {
    return <div>
        <button onClick={this.props.addProduct}>Add a product</button>
    </div>
}

// container part
function mapStateToProps(state) {
    return {...state};
}

function mapDispatchToProps(dispatch) {
    return bindActionCreators({
        ...ProductActions,
    }, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(Product);
这应该是答案
2021-05-22 15:58:18

我会尽量回答原来的问题...

智能和哑组件

在你的第一个问题中,你基本上首先问为什么bindActionCreators需要,以及什么样的组件不应该知道 Redux。

简而言之,这里的想法是组件应该分为智能(容器)和(展示)组件。 哑组件在需要知道的基础上工作。他们的灵魂工作是将给定的数据呈现为 HTML,仅此而已。他们不应该知道应用程序的内部工作原理。它们可以被视为您应用程序的皮肤深层前层。

另一方面,智能组件是一种粘合剂,它为组件准备数据,并且最好不进行 HTML 渲染。

这种架构促进了 UI 层和下面的数据层之间的松散耦合。这反过来又允许用其他东西(即 UI 的新设计)轻松替换两层中的任何一层,而不会破坏另一层。

回答你的问题:愚蠢的组件不应该知道 Redux(或任何不必要的数据层实现细节),因为我们将来可能想用其他东西替换它。

您可以在Redux 手册中找到有关此概念的更多信息,并在Dan Abramov 的文章Presentational and Container Components 中找到更深入的信息

哪个例子更好

第二个问题是关于给定示例的优点/缺点。

在第一个示例中,动作创建者在单独的actionCreators文件/module中定义,这意味着它们可以在其他地方重用。这几乎是定义动作的标准方式。我真的看不出这有什么缺点。

第二个例子中定义了动作的创作者的内联,这具有多个缺点:

  • 动作创建者不能重用(显然)
  • 事情更冗长,这意味着可读性较差
  • 动作类型是硬编码的 - 最好将它们consts分开定义,以便它们可以在减速器中引用 - 这将减少输入错误的机会
  • 内联定义动作创建者违反推荐/预期的使用方式 - 如果您计划共享代码,这将使您的代码对社区的可读性稍差

第二个示例比第一个示例有一个优势- 编写速度更快!因此,如果您对代码没有更大的计划,那可能没问题。

我希望我设法澄清了一些事情......

一种可能的用途bindActionCreators()是将多个动作“映射”在一起作为单个props。

正常的调度如下所示:

将几个常见的用户操作映射到props。

const mapStateToProps = (state: IAppState) => {
  return {
    // map state here
  }
}
const mapDispatchToProps = (dispatch: Dispatch) => {
  return {
    userLogin: () => {
      dispatch(login());
    },
    userEditEmail: () => {
      dispatch(editEmail());
    },
  };
};
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);

在较大的项目中,分别映射每个调度可能会让人感到笨拙。如果我们有一堆彼此相关的动作,我们可以组合这些动作例如,执行各种不同用户相关操作的用户操作文件。我们可以使用bindActionCreators()代替 ,而不是将每个动作作为单独的调度调用dispatch

使用 bindActionCreators() 进行多次调度

导入所有相关操作。它们可能都在 redux 存储中的同一个文件中

import * as allUserActions from "./store/actions/user";

现在,而不是使用调度使用 bindActionCreators()

    const mapDispatchToProps = (dispatch: Dispatch) => {
      return {
           ...bindActionCreators(allUserActions, dispatch);
        },
      };
    };
    export default connect(mapStateToProps, mapDispatchToProps, 
    (stateProps, dispatchProps, ownProps) => {
      return {
        ...stateProps,
        userAction: dispatchProps
        ownProps,
      }
    })(MyComponent);

现在我可以使用 propuserAction来调用组件中的所有操作。

即: userAction.login() userAction.editEmail()this.props.userAction.login() this.props.userAction.editEmail()

注意:您不必将 bindActionCreators() 映射到单个props。=> {return {}}映射到 的附加项userAction)。您还可以使用bindActionCreators()将单个文件的所有操作映射为单独的props。但我发现这样做可能会令人困惑。我更喜欢给每个动作或“动作组”一个明确的名称。我还想命名它们ownProps以更详细地描述这些“儿童props”是什么或它们来自哪里。当使用 Redux + React 时,在提供所有 props 的地方可能会有点混乱,所以描述性越好。