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

用于bindActionCreators的Redux文档指出:

唯一的用例bindActionCreators是,当您要将一些操作创建者传递给一个不了解Redux的组件,并且您不想将调度或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
      })
    }
  }
}
卡卡西达蒙西里2020/03/13 00:22:36

的一种可能用法bindActionCreators()是将多个动作“映射”在一起作为一个道具。

常规调度如下所示:

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

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

现在,我可以使用道具userAction来调用组件中的所有动作。

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

注意:您不必将bindActionCreators()映射到单个道具。=> {return {}}映射到的其他 userAction)。您还可以bindActionCreators()将单个文件的所有操作映射为单独的道具。但是我发现这样做可能会造成混淆。我更喜欢为每个动作或“动作组”指定一个明确的名称。我还想命名该名称,ownProps以更加描述这些“儿童道具”的含义或来源。当使用Redux + React时,会在提供所有道具的地方有些混乱,因此描述性越强越好。

达蒙阿飞斯丁2020/03/13 00:22:36

99%的时间,它与React-Redux connect()函数一起用作mapDispatchToProps参数的一部分可以在mapDispatch您提供函数中显式地使用它,如果使用对象速记语法并将充满动作创建者的对象传递给,则可以自动使用它connect

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

我在博客文章Idiomatic Redux中写了一些关于此主题的想法:为什么使用动作创建者?

Harry小宇宙2020/03/13 00:22:36

我将尝试回答原始问题...

智能和哑巴组件

在第一个问题中,您基本上会问bindActionCreators首先需要为什么,以及什么样的组件不应该意识到Redux。

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

另一方面,智能组件是一种胶水,它为愚蠢的组件准备数据,并且最好不进行HTML渲染。

这种体系结构促进了UI层与下面的数据层之间的松散耦合。从而可以轻松地用其他东西(即,UI的新设计)替换两层中的任何一层,而不会破坏另一层。

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

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

哪个例子更好

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

在第一个示例中,动作创建者在单独的actionCreators文件/模块中定义,这意味着它们可以在其他地方重用。这几乎是定义动作的标准方法。我真的没有看到任何缺点。

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

  • 动作创建者无法重复使用(显然)
  • 事情比较冗长,意味着可读性降低
  • 操作类型是硬编码的-最好将它们consts分别定义,以便可以在化简器中引用它们-这样可以减少键入错误的机会
  • 内联定义动作创建者违反了推荐/预期的使用方式-如果计划共享代码,这会使社区对代码的可读性降低

第二个示例比第一个示例具有一个优势 -编写起来更快!因此,如果您没有更好的代码计划,那可能很好。

我希望我能澄清一些事情...

村村AL2020/03/13 00:22:36

一个很好的用例bindActionCreators使用redux-saga-routinesredux-saga集成例如:

// routines.js
import { createRoutine } from "redux-saga-routines";
export const fetchPosts = createRoutine("FETCH_POSTS");
// Posts.js
import React from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { fetchPosts } from "routines";

class Posts extends React.Component {
  componentDidMount() {
    const { fetchPosts } = this.props;
    fetchPosts();
  }

  render() {
    const { posts } = this.props;
    return (
      <ul>
        {posts.map((post, i) => (
          <li key={i}>{post}</li>
        ))}
      </ul>
    );
  }
}

const mapStateToProps = ({ posts }) => ({ posts });
const mapDispatchToProps = dispatch => ({
  ...bindActionCreators({ fetchPosts }, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Posts);
// reducers.js
import { fetchPosts } from "routines";

const initialState = [];

export const posts = (state = initialState, { type, payload }) => {
  switch (type) {
    case fetchPosts.SUCCESS:
      return payload.data;
    default:
      return state;
  }
};
// api.js
import axios from "axios";

export const JSON_OPTS = { headers: { Accept: "application/json" } };
export const GET = (url, opts) =>
  axios.get(url, opts).then(({ data, headers }) => ({ data, headers }));
// sagas.js
import { GET, JSON_OPTS } from "api";
import { fetchPosts } from "routines";
import { call, put, takeLatest } from "redux-saga/effects";

export function* fetchPostsSaga() {
  try {
    yield put(fetchPosts.request());
    const { data } = yield call(GET, "/api/posts", JSON_OPTS);
    yield put(fetchPosts.success(data));
  } catch (error) {
    if (error.response) {
      const { status, data } = error.response;
      yield put(fetchPosts.failure({ status, data }));
    } else {
      yield put(fetchPosts.failure(error.message));
    }
  } finally {
    yield put(fetchPosts.fulfill());
  }
}

export function* fetchPostsRequestSaga() {
  yield takeLatest(fetchPosts.TRIGGER, fetchPostsSaga);
}

请注意,可以使用React Hooks(从React 16.8开始)实现此模式

理查德Tony2020/03/13 00:22:35

我认为最流行的答案实际上并不能解决这个问题。

下面的所有示例基本上都执行相同的操作,并且遵循“无预绑定”概念。

// 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代码库中使用,并且我发现它相当混乱。

我认为混淆来自以下事实:文档中的所有示例react-redux使用,bindActionCreatorsbindActionCreators的文档 (如问题本身所引用)说不要将其与react-redux一起使用。

我猜答案是代码库中的一致性,但是我个人更喜欢在需要时分派中显式地包装动作