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)
}
function mapStateToProps(state) {
return {
posts: state.posts,
comments: state.comments
}
}
function mapDispatchToProps(dispatch) {
return {
someCallback: (postId, index) => {
dispatch({
type: 'REMOVE_COMMENT',
postId,
index
})
}
}
}
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? .
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.
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);
Je vais essayer de répondre aux questions initiales ...
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.
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:
consts
, afin qu'ils puissent être référencés dans des réducteurs - ce qui réduirait les risques d'erreur de frappeLe 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 ...
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.
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).
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);