Quand bindActionCreators serait-il utilisé dans react / redux?


92

Redux docs pour bindActionCreators indiquent que:

Le seul cas d'utilisation pour bindActionCreators est lorsque vous souhaitez transmettre certains créateurs d'action à un composant qui ne connaît pas Redux, et que vous ne souhaitez pas lui transmettre la répartition ou le magasin Redux.

Quel serait un exemple où bindActionCreators serait utilisé / nécessaire?

Quel type de composant ne serait pas au courant Redux ?

Quels sont les avantages / inconvénients des deux options?

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

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

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

contre

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

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

Réponses:


58

Je ne pense pas que la réponse la plus populaire réponde réellement à la question.

Tous les exemples ci-dessous font essentiellement la même chose et ne suivent pas le concept de «pré-liaison».

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


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


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

L'option #3est juste un raccourci pour l'option #1, donc la vraie question pourquoi on utiliserait option #1vs option #2. J'ai vu les deux être utilisés dans la base de code react-redux, et je trouve que c'est plutôt déroutant.

Je pense que la confusion vient du fait que tous les exemples de react-reduxdoc utilisent bindActionCreatorstandis que le doc pour bindActionCreators (comme cité dans la question elle-même) dit de ne pas l'utiliser avec react-redux.

Je suppose que la réponse est la cohérence dans la base de code, mais je préfère personnellement emballer explicitement les actions dans la répartition chaque fois que nécessaire.


2
en quoi l'option est-elle #3un raccourci pour l'option #1?
ogostos


@ArtemBernatskyi merci. , Tourne donc hors il y a 3 cas pour mapDispatchToProps: function, objectet disparus. Comment gérer chaque cas est défini ici
ogostos

J'ai cherché cette réponse. Merci.
Kishan Rajdev

En fait, je suis tombé sur le cas d'utilisation spécifique dont les documents React parlent maintenant de "transmettre certains créateurs d'action à un composant qui ne connaît pas Redux" parce que j'ai un composant simple connecté à un composant plus complexe et ajoutant la surcharge de reduxet connectet addDispatchToPropsau composant simple semble exagéré si je peux simplement passer un accessoire vers le bas. Mais pour autant que je sache, presque tous les cas pour mapDispatchles accessoires seront soit des options, #1soit #3mentionnés dans la réponse
icc97

48

99% du temps, il est utilisé avec la fonction React-Redux connect(), dans le cadre du mapDispatchToPropsparamètre. Il peut être utilisé explicitement dans la mapDispatchfonction que vous fournissez, ou automatiquement si vous utilisez la syntaxe abrégée de l'objet et passez un objet plein de créateurs d'action à connect.

L'idée est qu'en pré-liant les créateurs d'actions, le composant connect()auquel vous passez techniquement "ne sait" pas qu'il est connecté - il sait juste qu'il doit fonctionner this.props.someCallback(). D'un autre côté, si vous n'avez pas lié les créateurs d'action et que vous avez appelé this.props.dispatch(someActionCreator()), le composant "sait" maintenant qu'il est connecté car il s'attend props.dispatchà exister.

J'ai écrit quelques réflexions sur ce sujet dans mon article de blog Idiomatic Redux: Pourquoi utiliser des créateurs d'action? .


1
Mais c'est lié à 'mapDispatchToProps', ce qui est bien. Il semble que les créateurs d'actions contraignants soient en fait une chose négative / inutile car vous perdrez la définition de la fonction (TS ou Flow) parmi beaucoup d'autres choses comme le débogage. Sur mes nouveaux projets je ne l'utilise jamais et je n'ai pas eu de problème à ce jour. Utilisation également de Saga et de l'état de persistance. De plus, si vous appelez simplement des fonctions redux (vous obtenez un bon clic), je dirais que c'est encore plus propre, alors `` attacher '' des créateurs d'action, ce qui à mon avis est désordonné et inutile. Votre smart (généralement des composants d'écran) peut toujours utiliser Connect, uniquement pour les accessoires.
Oliver Dixon

19

Exemple plus complet, passez un objet plein de créateurs d'action à se connecter:

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

cela devrait être la réponse
Deen John

16

Je vais essayer de répondre aux questions originales ...

Composants intelligents et stupides

Dans votre première question, vous demandez essentiellement pourquoi bindActionCreatorsest nécessaire en premier lieu, et quel type de composants ne doit pas être conscient de Redux.

En bref, l'idée ici est que les composants doivent être divisés en composants intelligents (conteneur) et stupides (présentation). Les composants stupides fonctionnent sur la base du besoin de savoir. Leur travail d'âme est de rendre les données données en HTML et rien de plus. Ils ne doivent pas être conscients du fonctionnement interne de l'application. Ils peuvent être considérés comme la couche avant profonde de la peau de votre application.

D'autre part, les composants intelligents sont une sorte de colle, qui prépare les données pour les composants stupides et ne fait de préférence pas de rendu HTML.

Ce type d'architecture favorise un couplage lâche entre la couche d'interface utilisateur et la couche de données en dessous. Cela permet à son tour de remplacer facilement l'une des deux couches par autre chose (c'est-à-dire une nouvelle conception de l'interface utilisateur), qui ne cassera pas l'autre couche.

Pour répondre à votre question: les composants stupides ne devraient pas être conscients de Redux (ou de tout détail d'implémentation inutile de la couche de données d'ailleurs) car nous pourrions vouloir le remplacer par autre chose à l'avenir.

Vous pouvez en savoir plus sur ce concept dans le manuel Redux et plus en détail dans l'article Presentational and Container Components de Dan Abramov.

Quel exemple est le meilleur

La seconde question portait sur les avantages / inconvénients des exemples donnés.

Dans le premier exemple, les créateurs d'actions sont définis dans un actionCreatorsfichier / module séparé , ce qui signifie qu'ils peuvent être réutilisés ailleurs. C'est à peu près la manière standard de définir les actions. Je ne vois vraiment aucun inconvénient à cela.

Le deuxième exemple définit les créateurs d'actions en ligne, ce qui présente plusieurs inconvénients:

  • les créateurs d'action ne peuvent pas être réutilisés (évidemment)
  • la chose est plus verbeuse, ce qui se traduit par moins lisible
  • les types d'action sont codés en dur - il est préférable de les définir constsséparément, afin qu'ils puissent être référencés dans des réducteurs - ce qui réduirait les risques d'erreurs de frappe
  • la définition des créateurs d'actions en ligne va à l'encontre de la manière recommandée / attendue de les utiliser - ce qui rendra votre code un peu moins lisible pour la communauté, au cas où vous prévoyez de partager votre code

Le deuxième exemple présente un avantage par rapport au premier: il est plus rapide à écrire! Donc, si vous n'avez pas de meilleurs plans pour votre code, cela pourrait être très bien.

J'espère avoir réussi à clarifier un peu les choses ...


2

Une utilisation possible de bindActionCreators()est de «mapper» plusieurs actions ensemble comme un seul accessoire.

Un envoi normal ressemble à ceci:

Mappez quelques actions utilisateur courantes sur des accessoires.

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

Dans les projets plus importants, la cartographie de chaque expédition séparément peut sembler lourde. Si nous avons un ensemble d'actions qui sont liées les unes aux autres, nous pouvons combiner ces actions . Par exemple, un fichier d'action utilisateur qui effectue toutes sortes d'actions différentes liées à l'utilisateur. Au lieu d'appeler chaque action comme une distribution distincte, nous pouvons utiliser à la bindActionCreators()place de dispatch.

Dépêches multiples utilisant bindActionCreators ()

Importez toutes vos actions associées. Ils sont probablement tous dans le même fichier dans le magasin redux

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

Et maintenant, au lieu d'utiliser la répartition, utilisez bindActionCreators ()

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

Maintenant, je peux utiliser l'accessoire userActionpour appeler toutes les actions de votre composant.

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

REMARQUE: vous ne devez pas mapper le bindActionCreators () à un seul accessoire. (Le supplémentaire => {return {}}qui correspond à userAction). Vous pouvez également utiliser bindActionCreators()pour mapper toutes les actions d'un seul fichier en tant qu'accessoires séparés. Mais je trouve que faire cela peut être déroutant. Je préfère que chaque action ou "groupe d'action" reçoive un nom explicite. J'aime aussi nommer le ownPropspour être plus descriptif sur ce que sont ces "accessoires pour enfants" ou d'où ils viennent. Lorsque vous utilisez Redux + React, il peut être un peu déroutant de savoir où tous les accessoires sont fournis, donc plus ils sont descriptifs, mieux c'est.


2

en utilisant bindActionCreators, il peut regrouper plusieurs fonctions d'action et les transmettre à un composant qui ne connaît pas Redux (Dumb Component) comme ceci

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

1

Je cherchais également à en savoir plus sur bindActionsCreators et voici comment je l'ai implémenté dans mon projet.

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

const logoutRequest = () => {
 return {
   type: 'LOGOUT_REQUEST'
  }
}

export default { loginRequest, logoutRequest };

Dans votre composant React

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

Un cas d'utilisation intéressant pour bindActionCreatorsest l'intégration avec redux-saga en utilisant redux-saga-routines . Par exemple:

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

Notez que ce modèle peut être implémenté en utilisant React Hooks (à partir de React 16.8).


-1

La déclaration de la documentation est très claire:

Le seul cas d'utilisationbindActionCreators est lorsque vous souhaitez transmettre certains créateurs d'action à un composant qui ne connaît pas Redux, et que vous ne souhaitez pas lui transmettre la répartition ou le magasin Redux.

Il s'agit clairement d'un cas d'utilisation qui peut survenir dans les conditions suivantes et dans une seule condition:

Disons que nous avons les composants A et 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

Injecter pour react-redux: (A)

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

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

Injecté par react-redux: (B)

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

Vous avez remarqué ce qui suit?

  • Nous avons mis à jour le magasin redux via le composant A alors que nous ne connaissions pas le magasin redux dans le composant B.

  • Nous ne mettons pas à jour dans le composant A. Pour savoir ce que je veux dire exactement, vous pouvez explorer ce post . J'espère que vous aurez une idée.


7
Je n'ai rien compris
vsync
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.