Dites que j'ai ce qui suit:
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
}
}
Et dans ce créateur d'action, je souhaite accéder à l'état du magasin global (tous les réducteurs). Est-il préférable de faire ceci:
import store from '../store';
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
ou ca:
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return (dispatch, getState) => {
const {items} = getState().otherReducer;
dispatch(anotherAction(items));
}
}
Les opinions divergent quant à savoir si l'accès aux créateurs d'état dans l'action est une bonne idée:
state.something.items
dans un créateur d'action est définitivement un anti-motif et est déconseillé car il occultait l'historique des modifications: s'il y a un bogue et que items
sont incorrects, il est difficile de retracer d'où proviennent ces valeurs incorrectes car elles font déjà partie de l'action plutôt que d'être calculées directement par un réducteur en réponse à une action. Faites-le avec précaution. "getState
dans thunks - c'est pourquoi il existe . Il discute des avantages et des inconvénients de l'accès à l'état en action des créateurs dans son billet de blog Idiomatic Redux: réflexions sur les thunks, les sagas, l'abstraction et la réutilisabilité .Si vous trouvez que vous en avez besoin, les deux approches que vous avez suggérées sont acceptables. La première approche ne nécessite aucun middleware:
import store from '../store';
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
Cependant, vous pouvez voir que cela dépend de store
étant un singleton exporté à partir d'un module. Nous ne recommandons pas cette option , car il est beaucoup plus difficile de ajouter le rendu du serveur à votre application car dans la plupart des cas sur le serveur, vous souhaiterez avoir un magasin séparé par demande . Donc, bien que techniquement cette approche fonctionne, nous ne recommandons pas d’exporter un magasin à partir d’un module.
C'est pourquoi nous recommandons la deuxième approche:
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return (dispatch, getState) => {
const {items} = getState().otherReducer;
dispatch(anotherAction(items));
}
}
Cela nécessiterait que vous utilisiez le middleware Redux Thunk mais cela fonctionne très bien sur le client et sur le serveur. Vous pouvez en savoir plus sur Redux Thunk et pourquoi il est nécessaire dans ce cas ici .
Idéalement, vos actions ne devraient pas être "grasses" et contenir le moins d’informations possible, mais vous devriez vous sentir libre de faire ce qui vous convient le mieux dans votre propre application. Le Redux FAQ a des informations sur division de la logique entre les créateurs d’action et les réducteurs et moments où il peut être utile d’utiliser getState
dans un créateur d’action =.
Lorsque votre scénario est simple, vous pouvez utiliser
import store from '../store';
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
Mais parfois, votre action creator
doit déclencher plusieurs actions
par exemple, une requête asynchrone nécessite donc des actions REQUEST_LOAD
REQUEST_LOAD_SUCCESS
REQUEST_LOAD_FAIL
export const [REQUEST_LOAD, REQUEST_LOAD_SUCCESS, REQUEST_LOAD_FAIL] = [`REQUEST_LOAD`
`REQUEST_LOAD_SUCCESS`
`REQUEST_LOAD_FAIL`
]
export function someAction() {
return (dispatch, getState) => {
const {
items
} = getState().otherReducer;
dispatch({
type: REQUEST_LOAD,
loading: true
});
$.ajax('url', {
success: (data) => {
dispatch({
type: REQUEST_LOAD_SUCCESS,
loading: false,
data: data
});
},
error: (error) => {
dispatch({
type: REQUEST_LOAD_FAIL,
loading: false,
error: error
});
}
})
}
}
Remarque: vous avez besoin de redux-thunk pour renvoyer une fonction dans le créateur de l'action
Je tiens à préciser qu’il n’est pas si mal de lire dans le magasin - il serait peut-être beaucoup plus pratique de décider ce qui doit être fait en fonction du magasin, que de tout transférer au composant, puis en tant que paramètre de une fonction. Je suis tout à fait d’accord avec Dan pour dire qu’il est bien préférable de ne pas utiliser store en tant que singletone, sauf si vous êtes sûr à 100% de l’utiliser uniquement pour le rendu côté client (sinon, des bugs difficiles à détecter pourraient apparaître).
J'ai créé une bibliothèque récemment pour traiter la verbosité de redux, et je pense que c'est une bonne idée de tout mettre dans le middleware, afin que vous ayez chaque chose comme injection de dépendance.
Alors, votre exemple ressemblera à ça:
import { createSyncTile } from 'redux-tiles';
const someTile = createSyncTile({
type: ['some', 'tile'],
fn: ({ params, selectors, getState }) => {
return {
data: params.data,
items: selectors.another.tile(getState())
};
},
});
Cependant, comme vous pouvez le constater, nous ne modifions pas vraiment les données ici. Il y a donc de fortes chances pour que nous puissions simplement utiliser ce sélecteur ailleurs pour le combiner ailleurs.
Je suis d'accord avec @Bloomca. Transmettre la valeur nécessaire du magasin à la fonction de répartition en tant qu'argument semble plus simple que d'exporter le magasin. J'ai fait un exemple ici:
import React from "react";
import {connect} from "react-redux";
import * as actions from '../actions';
class App extends React.Component {
handleClick(){
const data = this.props.someStateObject.data;
this.props.someDispatchFunction(data);
}
render(){
return (
<div>
<div onClick={ this.handleClick.bind(this)}>Click Me!</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return { someStateObject: state.someStateObject };
};
const mapDispatchToProps = (dispatch) => {
return {
someDispatchFunction:(data) => { dispatch(actions.someDispatchFunction(data))},
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Présenter un autre moyen de résoudre ce problème. Cela peut être meilleur ou pire que la solution de Dan, selon votre application.
Vous pouvez obtenir l'état des réducteurs dans les actions en fractionnant l'action en deux fonctions distinctes: demander d'abord les données, ensuite agir sur les données. Vous pouvez le faire en utilisant redux-loop
.
D'abord, merci de demander les données.
_export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
}
}
_
Dans le réducteur, interceptez la demande et fournissez les données à la deuxième étape en utilisant _redux-loop
_.
_import { loop, Cmd } from 'redux-loop';
const initialState = { data: '' }
export default (state=initialState, action) => {
switch(action.type) {
case SOME_ACTION: {
return loop(state, Cmd.action(anotherAction(state.data))
}
}
}
_
Avec les données en main, faites ce que vous vouliez au départ
_export const ANOTHER_ACTION = 'ANOTHER_ACTION';
export function anotherAction(data) {
return {
type: ANOTHER_ACTION,
payload: data,
}
}
_
J'espère que ça aide quelqu'un.
Je sais que je suis en retard à la fête ici, mais je suis venu ici pour des opinions sur mon propre désir d'utiliser l'État dans des actions, puis j'ai formé le mien, quand j'ai réalisé ce que je pensais être le comportement correct.
C'est là qu'un sélecteur me semble le plus logique. Votre composant qui émet cette demande doit être informé du moment opportun pour l’émettre par sélection.
export const SOME_ACTION = 'SOME_ACTION';
export function someAction(items) {
return (dispatch) => {
dispatch(anotherAction(items));
}
}
Cela peut sembler être une fuite d’abstractions, mais votre composant doit clairement envoyer un message et le contenu du message doit contenir l’état pertinent. Malheureusement, votre question ne contient pas d'exemple concret, car nous pourrions travailler sur un "meilleur modèle" de sélecteurs et d'actions de cette manière.