When would bindActionCreators be used in react/redux?

后端 未结 10 1065
温柔的废话
温柔的废话 2020-12-07 11:04

Redux docs for bindActionCreators states that:

The only use case for bindActionCreators is when you want to pass some action cre

相关标签:
10条回答
  • 2020-12-07 11:48

    I was also looking for to know more about bindActionsCreators and here is how i implemented in my project.

    // Actions.js
    // Action Creator
    const loginRequest = (username, password) => {
     return {
       type: 'LOGIN_REQUEST',
       username,
       password,
      }
    }
    
    const logoutRequest = () => {
     return {
       type: 'LOGOUT_REQUEST'
      }
    }
    
    export default { loginRequest, logoutRequest };
    

    In your React Component

    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import { bindActionCreators } from 'redux';
    import ActionCreators from './actions'
    
    class App extends Component {
      componentDidMount() {
       // now you can access your action creators from props.
        this.props.loginRequest('username', 'password');
      }
    
      render() {
        return null;
      }
    }
    
    const mapStateToProps = () => null;
    
    const mapDispatchToProps = dispatch => ({ ...bindActionCreators(ActionCreators, dispatch) });
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps,
    )(App);
    
    0 讨论(0)
  • 2020-12-07 11:49

    One nice use case for bindActionCreators is for integration with redux-saga using redux-saga-routines. For example:

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

    Note that this pattern could be implemented using React Hooks (as of React 16.8).

    0 讨论(0)
  • 2020-12-07 11:53

    One possible use of bindActionCreators() is to "map" multiple actions together as a single prop.

    A normal dispatch looks like this:

    Map a couple common user actions to 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);
    

    In larger projects mapping each dispatch separately can feel unwieldy. If we have a bunch of actions that are related to each other we can combine these actions. For example a user action file that did all kinds of different user related actions. Instead of calling each action as a separate dispatch we can use bindActionCreators() instead of dispatch.

    Multiple Dispatches using bindActionCreators()

    Import all your related actions. They are likely all in the same file in the redux store

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

    And now instead of using dispatch use bindActionCreators()

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

    Now I can use the prop userAction to call all the actions in your component.

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

    NOTE: You do not have to map the bindActionCreators() to a single prop. (The additional => {return {}} that maps to userAction). You can also use bindActionCreators() to map all the actions of a single file as separate props. But I find doing that can be confusing. I prefer having each action or "action group" be given an explicit name. I also like to name the ownProps to be more descriptive about what these "child props" are or where they are coming from. When using Redux + React it can get a bit confusing where all the props are being supplied so the more descriptive the better.

    0 讨论(0)
  • 2020-12-07 11:55

    The docs statement is very clear:

    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.

    This is clearly a use case that may arise in the following and only one condition:

    Lets say, we have component A and 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
    

    Inject to react-redux: (A)

    const boundActionCreators = bindActionCreators(SomeActionCreator, dispatch)
    // myActionCreatorMethod,
    // myActionCreatorMethod2,
    // myActionCreatorMethod3,
    
    // when we want to dispatch
    const action = SomeActionCreator.myActionCreatorMethod('My updates')
    dispatch(action)
    

    Injected by react-redux: (B)

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

    Noticed of the following?

    • We updated the redux store through the component A whilst we were unknown of redux store in component B.

    • We're not updating in component A. To know what exactly I mean, you may explore this post. I hope you'll have an idea.

    0 讨论(0)
提交回复
热议问题