web-dev-qa-db-fra.com

Quand bindActionCreators sera-t-il utilisé dans react / redux?

Redux docs pour bindActionCreators déclare que:

Le seul cas d'utilisation de bindActionCreators concerne le transfert de certains créateurs d'action à un composant non conscient de Redux et le refus de la répartition ou du magasin Redux.

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

Quel type de composant ne serait pas au courant de 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
      })
    }
  }
}
76
d.code

99% du temps, il est utilisé avec la fonction React-Redux connect() dans le paramètre mapDispatchToProps. Il peut être utilisé explicitement à l'intérieur de la fonction mapDispatch que vous fournissez ou automatiquement si vous utilisez la syntaxe abrégée de l'objet et transmettez un objet rempli de créateurs d'actions à connect.

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

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

47
markerikson

Je ne pense pas que la réponse la plus populaire, aborde réellement la question.

Tous les exemples ci-dessous font essentiellement la même chose et suivent le concept de non "pré-consolidation".

// 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 #3 n'est qu'un raccourci pour l'option #1, c'est donc la vraie question de savoir pourquoi on utiliserait l'option #1 contre l'option #2. Je les ai vus tous les deux utilisés dans react-redux codebase, et je trouve que c'est assez déroutant.

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

Je suppose que la réponse est la cohérence dans le code, mais je préfère personnellement encapsuler explicitement les actions dans dispatch chaque fois que cela est nécessaire.

39
Diana Suvorova

Exemple plus complet, transmettez un objet rempli de créateurs d’actions à 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);
13
Charlie 木匠

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

Composants Smart & Dumb

Dans votre première question, vous demandez en gros pourquoi bindActionCreators est nécessaire et quels types de composants ne doivent pas être conscients de Redux.

En bref, l’idée ici est que les composants doivent être scindés en intelligent (conteneur) et en muet composants (présentation). Les composants stupides fonctionnent sur la base du besoin de savoir. Leur travail consiste à rendre les données données au format HTML et rien de plus. Ils ne doivent pas être conscients du fonctionnement interne de l'application. Ils peuvent être vus comme la couche avant profonde de votre application.

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

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

Pour répondre à votre question, les composants stupides ne devraient pas être au courant de Redux (ni d'aucun détail d'implémentation inutile de la couche de données), car nous pourrions vouloir le remplacer par quelque chose d'autre à l'avenir.

Vous pouvez trouver plus d'informations sur ce concept dans le manuel Redux et plus en détail dans l'article Composants de présentation et de conteneur de Dan Abramov.

Quel exemple est le meilleur

La deuxième question portait sur les avantages/inconvénients des exemples donnés.

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

Le deuxième exemple définit les créateurs d'action en ligne, ce qui présente de nombreux 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 séparément consts, afin qu'ils puissent être référencés dans des réducteurs - ce qui réduirait les risques d'erreur de frappe
  • définir les créateurs d'action en ligne va à l'encontre de la manière recommandée/prévue de les utiliser - ce qui rendra votre code un peu moins lisible pour la communauté, au cas où vous envisagez de le partager.

Le deuxième exemple a un avantage par rapport au premier - il est plus rapide d’écrire! Donc, si vous n'avez pas de projets plus ambitieux pour votre code, tout ira bien.

J'espère que j'ai réussi à clarifier un peu les choses ...

12
knee-cola

Une utilisation possible de bindActionCreators() consiste à "mapper" plusieurs actions en un seul accessoire.

ne dépêche normale ressemble à ceci:

Mappez quelques actions utilisateur communes 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, cartographier chaque dépêche séparément peut sembler compliqué. Si nous avons plusieurs actions qui sont liées les unes aux autres, nous pouvons combiner ces actions. Par exemple, un fichier d’actions utilisateur qui a effectué toutes sortes d’actions différentes liées aux utilisateurs. Au lieu d'appeler chaque action séparément, nous pouvons utiliser bindActionCreators() au lieu de dispatch.

Plusieurs dépêches 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 dispatch, 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 le prop userAction pour 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 => {return {}} supplémentaire qui mappe sur userAction). Vous pouvez également utiliser bindActionCreators() pour mapper toutes les actions d'un fichier en tant qu'éléments séparés. Mais je trouve que 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 la ownProps pour être plus descriptive sur ce que sont ces "accessoires pour enfants" ou leur origine. Lorsque vous utilisez Redux + React _, il peut être un peu déroutant de savoir où tous les accessoires sont fournis. Plus le descriptif est bon, mieux c'est.

0
matthew

Un cas d'utilisation de Nice pour bindActionCreators concerne 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).

0
tomc

Je cherchais également à en savoir plus sur bindActionsCreators et voici comment j'ai implémenté 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 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
Bimal Grg