web-dev-qa-db-fra.com

Transition vers une autre route en cas d'action redux asynchrone réussie

J'ai un ensemble assez simple de composants de réaction:

  • container qui se raccorde à redux et gère les actions, les abonnements aux magasins, etc.
  • list qui affiche une liste de mes articles
  • new qui est un formulaire pour ajouter un nouvel élément à la liste

J'ai quelques react-router routes comme suit:

<Route name='products' path='products' handler={ProductsContainer}>
  <Route name='productNew' path='new' handler={ProductNew} />
  <DefaultRoute handler={ProductsList} />
</Route>

de sorte que soit la list soit la form sont montrés mais pas les deux.

Ce que j'aimerais faire, c'est que l'application revienne à la liste une fois qu'un nouvel élément a été ajouté avec succès. 

Ma solution jusqu'ici consiste à avoir une .then() après la async dispatch:

dispatch(actions.addProduct(product)
  .then(this.transitionTo('products'))
)

Est-ce la bonne façon de faire ou devrais-je déclencher une autre action pour déclencher le changement d'itinéraire?

56
Clarkie

Si vous ne souhaitez pas utiliser une solution plus complète telle que Redux Router , vous pouvez utiliser Transitions de l'historique Redux qui vous permet d'écrire du code comme celui-ci:

export function login() {
  return {
    type: LOGGED_IN,
    payload: {
      userId: 123
    }
    meta: {
      transition: (state, action) => ({
        path: `/logged-in/${action.payload.userId}`,
        query: {
          some: 'queryParam'
        },
        state: {
          some: 'state'
        }
      })
    }
  };
}

Ceci est similaire à ce que vous avez suggéré mais un peu plus sophistiqué. Il utilise toujours la même bibliothèque historique sous le capot, ce qui le rend compatible avec React Router.

28
Dan Abramov

J'ai fini par créer un middleware super simple qui ressemble à ça:

import history from "../routes/history";

export default store => next => action => {

    if ( ! action.redirect ) return next(action);

    history.replaceState(null, action.redirect);
}

Donc, à partir de là, vous devez simplement vous assurer que vos actions successful ont une propriété redirect. Notez également que ce middleware ne déclenche pas next(). C'est intentionnel, car une transition d'itinéraire devrait être la fin de la chaîne d'action.

25
silkAdmin

Pour ceux qui utilisent une couche d'API middleware pour résumer leur utilisation de quelque chose comme isomorphic-fetch , et qui utilisent également redux-thunk , vous pouvez simplement chaîner votre promesse dispatch dans vos actions , ainsi:

import { Push } from 'react-router-redux';
const USER_ID = // imported from JWT;

function fetchUser(primaryKey, opts) {
    // this prepares object for the API middleware
}

// this can be called from your container
export function updateUser(payload, redirectUrl) {
    var opts = {
        method: 'PUT',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(payload)
    };
    return (dispatch) => {
        return dispatch(fetchUser(USER_ID, opts))
            .then((action) => {
                if (action.type === ActionTypes.USER_SUCCESS) {
                    dispatch(Push(redirectUrl));
                }
            });
    };
}

Cela réduit le besoin d'ajouter des bibliothèques dans votre code comme suggéré ici , et co-localise également vos actions avec leurs redirections comme dans redux-history-transitions .

Voici à quoi ressemble mon magasin:

import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers';
import thunk from 'redux-thunk';
import api from '../middleware/api';
import { routerMiddleware } from 'react-router-redux';

export default function configureStore(initialState, browserHistory) {
    const store = createStore(
        rootReducer,
        initialState,
        applyMiddleware(thunk, api, routerMiddleware(browserHistory))
    );

    return store;
}
18
Cameron

Je sais que je suis un peu en retard dans la soirée, car la navigation de réaction est déjà incluse dans la documentation de réaction-native, mais elle peut néanmoins être utile pour les utilisateurs qui ont utilisé/utilisé Navigator api dans leurs applications. petit hack, je sauve une instance de navigateur dans un objet dès que renderScene se produit- 

renderScene(route, navigator) {
      const Component = Routes[route.Name]
      api.updateNavigator(navigator); //will allow us to access navigator anywhere within the app
      return <Component route={route} navigator={navigator} data={route.data}/>

}

mon fichier api est quelque chose comme ça

'use strict';

//this file includes all my global functions
import React, {Component} from 'react';
import {Linking, Alert, NetInfo, Platform} from 'react-native';
var api = {
    navigator,
    isAndroid(){
        return (Platform.OS === 'Android');
    },
    updateNavigator(_navigator){
      if(_navigator)
          this.navigator = _navigator;
    },
}

module.exports = api;

maintenant, dans vos actions, vous pouvez simplement appeler 

api.navigator.Push ({Nom: 'nom de route', données: WHATEVER_YOU_WANTED_TO_PASS)

il vous suffit d'importer votre API depuis le module.

0
Manjeet Singh

Si vous utilisez react-redux et react-router , alors je pense ce lien fournit une excellente solution.

Voici les étapes que j'ai suivies:

  • Transmettez un accessoire rea-routeur history à votre composant, soit en le rendant dans un composant rea-router <Route/>, soit en créant un composant d'ordre supérieur à l'aide de withRouter.
  • Créez ensuite l’itinéraire que vous souhaitez rediriger (j’ai appelé le mien to).
  • Troisièmement, appelez votre action redux avec history et to.
  • Enfin, lorsque vous souhaitez rediriger (par exemple, lorsque votre action redux est résolue), appelez history.Push(to).
0
Brad W