Actions Redux de tir en réponse aux transitions de routage dans React Router

J’utilise reactrouter et redux dans ma dernière application et je suis confronté à quelques problèmes liés aux modifications d’état requirejses en fonction des parameters et requêtes url actuels.

Fondamentalement, j’ai un composant qui doit mettre à jour son état chaque fois que l’URL change. L’état est passé à travers les accessoires par redux avec le décorateur comme ça

@connect(state => ({ campaigngroups: state.jobresults.campaigngroups, error: state.jobresults.error, loading: state.jobresults.loading })) 

En ce moment, j’utilise la méthode du cycle de vie de componentWillReceiveProps pour répondre aux changements d’URL provenant de react-router, car react-router transmettra de nouveaux objects au gestionnaire lorsque l’url changera dans this.props.params et this.props.query – le Le principal problème avec cette approche est que je déclenche une action dans cette méthode pour mettre à jour l’état – qui transmet ensuite les nouveaux accessoires au composant qui déclenchera à nouveau la même méthode de cycle de vie – créant ainsi une boucle sans fin. variable d’état pour empêcher que cela se produise.

  componentWillReceiveProps(nextProps) { if (this.state.shouldupdate) { let { slug } = nextProps.params; let { citizenships, discipline, workright, location } = nextProps.query; const params = { slug, discipline, workright, location }; let filters = this._getFilters(params); // set the state accroding to the filters in the url this._setState(params); // sortinggger the action to refill the stores this.actions.loadCampaignGroups(filters); } } 

Existe-t-il une approche standard pour déclencher des actions basées sur des transitions d’itinéraires OU puis-je avoir l’état du magasin directement connecté à l’état du composant au lieu de le faire passer par des accessoires? J’ai essayé d’utiliser la méthode statique willTransitionTo mais je n’ai pas access à this.props.dispatch.

Bon, j’ai finalement trouvé une réponse sur la page github du redux, donc je la posterai ici. J’espère que ça sauve quelqu’un.

@deowk Il y a deux parties à ce problème, je dirais. La première est que componentWillReceiveProps () n’est pas un moyen idéal pour répondre aux changements d’état – principalement parce que cela vous oblige à penser impérativement, au lieu de réagir comme nous le faisons avec Redux. La solution consiste à stocker vos informations de routeur actuelles (emplacement, parameters, requête) dans votre magasin. Ensuite, tout votre état est au même endroit et vous pouvez vous y abonner en utilisant la même API Redux que le rest de vos données.

L’astuce consiste à créer un type d’action qui se déclenche chaque fois que l’emplacement du routeur change. C’est facile dans la prochaine version 1.0 de React Router:

 // routeLocationDidUpdate() is an action creator // Only call it from here, nowhere else BrowserHistory.listen(location => dispatch(routeLocationDidUpdate(location))); 

Maintenant, l’état de votre magasin sera toujours en phase avec l’état du routeur. Cela corrige le besoin de réagir manuellement aux changements de requêtes et à setState () dans votre composant ci-dessus – utilisez simplement le connecteur Redux.

  ({ filter: getFilters(store.router.params) })} /> 

La deuxième partie du problème est que vous avez besoin d’un moyen de réagir aux changements d’état Redux en dehors de la couche de vue, par exemple pour déclencher une action en réponse à un changement d’itinéraire. Vous pouvez continuer à utiliser componentWillReceiveProps pour les cas simples comme celui que vous décrivez, si vous le souhaitez.

Pour tout ce qui est plus compliqué, je vous recommande d’utiliser RxJS si vous y êtes ouvert. C’est exactement ce que les observables sont conçus pour – un stream de données réactif.

Pour ce faire, dans Redux, créez d’abord une séquence observable d’états de magasin. Vous pouvez le faire en utilisant observableFromStore () de rx.

EDIT COMME SUGGÉRÉ PAR CNP

 import { Observable } from 'rx' function observableFromStore(store) { return Observable.create(observer => store.subscribe(() => observer.onNext(store.getState())) ) } 

Il suffit ensuite d’utiliser des opérateurs observables pour s’abonner à des changements d’état spécifiques. Voici un exemple de redirection à partir d’une page de connexion après une connexion réussie:

 const didLogin$ = state$ .distinctUntilChanged(state => !state.loggedIn && state.router.path === '/login') .filter(state => state.loggedIn && state.router.path === '/login'); didLogin$.subscribe({ router.transitionTo('/success'); }); 

Cette implémentation est beaucoup plus simple que la même fonctionnalité utilisant des motifs impératifs comme componentDidReceiveProps ().

Comme mentionné précédemment, la solution comprend deux parties:

1) Relier les informations de routage à l’état

Pour cela, il suffit de configurer react-router-redux . Suivez les instructions et vous irez bien.

Une fois que tout est réglé, vous devriez avoir un état de routing , comme ceci:

Etat

2) Observez les modifications de routage et déclenchez vos actions

Quelque part dans votre code, vous devriez avoir quelque chose comme ceci:

 // find this piece of code export default function configureStore(initialState) { // the logic for configuring your store goes here let store = createStore(...); // we need to bind the observer to the store <> } 

Ce que vous voulez faire, c’est observer les changements dans le magasin, vous pouvez donc dispatch actions lorsque quelque chose change.

Comme @deowk l’a mentionné, vous pouvez utiliser rx , ou vous pouvez écrire votre propre observateur:

reduxStoreObserver.js

 var currentValue; /** * Observes changes in the Redux store and calls onChange when the state changes * @param store The Redux store * @param selector A function that should return what you are observing. Example: (state) => state.routing.locationBeforeTransitions; * @param onChange A function called when the observable state changed. Params are store, previousValue and currentValue */ export default function observe(store, selector, onChange) { if (!store) throw Error('\'store\' should be truthy'); if (!selector) throw Error('\'selector\' should be truthy'); store.subscribe(() => { let previousValue = currentValue; try { currentValue = selector(store.getState()); } catch(ex) { // the selector could not get the value. Maybe because of a null reference. Let's assume undefined currentValue = undefined; } if (previousValue !== currentValue) { onChange(store, previousValue, currentValue); } }); } 

Maintenant, tout ce que vous avez à faire est d’utiliser le reduxStoreObserver.js nous venons d’écrire pour observer les changements:

 import observe from './reduxStoreObserver.js'; export default function configureStore(initialState) { // the logic for configuring your store goes here let store = createStore(...); observe(store, //if THIS changes, we the CALLBACK will be called state => state.routing.locationBeforeTransitions.search, (store, previousValue, currentValue) => console.log('Some property changed from ', previousValue, 'to', currentValue) ); } 

Le code ci-dessus appelle notre fonction à chaque fois que locationBeforeTransitions.search change dans l’état (suite à la navigation de l’utilisateur). Si vous le souhaitez, vous pouvez observer la chaîne de requête et ainsi de suite.

Si vous souhaitez déclencher une action suite à des modifications de routage, il suffit de store.dispatch(yourAction) dans le gestionnaire.