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

时间:2017-01-20 01:11:50

标签: reactjs redux react-redux

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

VS

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

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

9 个答案:

答案 0 :(得分:44)

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

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

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

答案 1 :(得分:35)

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

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

#3

选项#1只是选项#1的简写,因此真正的问题是为什么人们会使用选项#2与选项react-redux。我已经看到它们都在react-redux代码库中使用,我发现它相当令人困惑。

我认为混淆来自于bindActionCreators doc中的所有examples使用 private func randomName<T: Object & TextName>(from results: Results<T>, crossNameChance: Int, textRanges: [[CountableRange<Int>]]?) -> String bindActionCreators的文档(在问题本身中引用)说不要与react-redux一起使用它。

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

答案 2 :(得分:12)

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

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

答案 3 :(得分:10)

我会尝试回答原来的问题......

Smart&amp;愚蠢的组件

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

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

另一方面,智能组件是一种粘合剂,它为 dumb 组件准备数据,最好不进行HTML呈现。

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

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

你可以在Redux manual中找到更多关于这个概念的信息,并在Dan Abramov的文章Presentational and Container Components中更深入地了解。

哪个例子更好

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

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

第二个示例定义了内联的动作创建者,它有多个缺点:

  • 动作创作者无法重复使用(显然)
  • 事情更冗长,转化为不太可读
  • 操作类型是硬编码的 - 最好将它们分别定义为consts,以便可以在reducers中引用它们 - 这样可以减少输入错误的机会
  • 内联定义动作创建者是违反推荐/预期使用它们的方式 - 这将使您的代码对社区的可读性降低,以防您计划共享代码

第二个例子比第一个例子一个优势 - 写得更快!因此,如果您没有更好的代码计划,那可能就好了。

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

答案 4 :(得分:2)

通过使用bindActionCreators,它可以对多个动作函数进行分组,并将其向下传递给一个不知道Redux的组件(哑组件)

// actions.js

export const increment = () => ({
    type: 'INCREMENT'
})

export const decrement = () => ({
    type: 'DECREMENT'
})
// main.js
import { Component } from 'react'
import { bindActionCreators } from 'redux'
import * as Actions from './actions.js'
import Counter from './counter.js'

class Main extends Component {

  constructor(props) {
    super(props);
    const { dispatch } = props;
    this.boundActionCreators = bindActionCreators(Actions, dispatch)
  }

  render() {
    return (
      <Counter {...this.boundActionCreators} />
    )
  }
}
// counter.js
import { Component } from 'react'

export default Counter extends Component {
  render() {
    <div>
     <button onclick={() => this.props.increment()}
     <button onclick={() => this.props.decrement()}
    </div>
  }
}

答案 5 :(得分:0)

bindActionCreators的一个很好的用例是使用redux-sagaredux-saga-routines集成。例如:

// 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开始)实现此模式。

答案 6 :(得分:0)

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时,可能会混淆所有道具的提供位置,因此描述性越强越好。

答案 7 :(得分:0)

The only use case for bindActionCreators is when you want to pass some action creators down to a component that isn't aware of Redux, and you don't want to pass dispatch or the Redux store to it.

如果您将typescript与redux一起使用,这将非常有用。

答案 8 :(得分:-1)

docs语句非常清楚:

  

bindActionCreators的唯一用例是当您要将一些操作创建者传递给一个不了解Redux的组件,并且您不想将调度或Redux存储传递给该组件。

这显然是一个用例,可能会在以下情况下出现,并且只有一种情况:

可以说,我们有A和B部分:

// A use connect and updates the redux store
const A = props => {}
export default connect()(A)

// B doesn't use connect therefore it does not know about the redux store.
const B = props => {}
export default B

注入react-redux:(A)

const boundActionCreators = bindActionCreators(SomeActionCreator, dispatch)
// myActionCreatorMethod,
// myActionCreatorMethod2,
// myActionCreatorMethod3,

// when we want to dispatch
const action = SomeActionCreator.myActionCreatorMethod('My updates')
dispatch(action)

由react-redux注入:(B)

const { myActionCreatorMethod } = props
<B myActionCreatorMethod={myActionCreatorMethod} {...boundActionCreators} />

涉及以下内容?

  • 我们通过组件A更新了redux存储,而对于组件B中的redux存储却一无所知。

  • 我们不会在组件A中进行更新。要确切了解我的意思,您可以探索this post。我希望你有个主意。