Transition to another route on successful async re

2019-01-30 06:15发布

I have a pretty simple set of react components:

  • container that hooks into redux and handles the actions, store subscriptions, etc
  • list which displays a list of my items
  • new which is a form to add a new item to the list

I have some react-router routes as follows:

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

so that either the list or the form are shown but not both.

What I'd like to do is to have the application re-route back to the list once a new item has been successfully added.

My solution so far is to have a .then() after the async dispatch:

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

Is this the correct way to do this or should I fire another action somehow to trigger the route change?

4条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-01-30 06:32

For those that are using a middleware API layer to abstract their usage of something like isomorphic-fetch, and also happen to be using redux-thunk, you can simply chain off your dispatch Promise inside of your actions, like so:

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));
                }
            });
    };
}

This reduces the need for adding libraries into your code as suggested here, and also nicely co-locates your actions with their redirects as done in redux-history-transitions.

Here is what my store looks like:

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;
}
查看更多
叼着烟拽天下
3楼-- · 2019-01-30 06:42

If you don't want to use a more complete solution like Redux Router, you can use Redux History Transitions which lets you write code like this:

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'
        }
      })
    }
  };
}

This is similar to what you suggested but a tiny bit more sophisticated. It still uses the same history library under the hood so it's compatible with React Router.

查看更多
啃猪蹄的小仙女
4楼-- · 2019-01-30 06:42

I know I am little late in the party as react-navigation is already included in the react-native documentation, but still it can be useful for the user who have used/using Navigator api in their apps. what I tried is little hackish, I am saving navigator instance in object as soon as renderScene happens-

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}/>

}

my api file is something lke this

'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;

now in your actions you can simply call

api.navigator.push({Name:'routeName', data:WHATEVER_YOU_WANTED_TO_PASS)

you just need to import your api from the module.

0

If you're using react-redux and react-router, then I think this link provides a great solution.

Here's the steps I used:

  • Pass in a react-router history prop to your component, either by rendering your component inside a react-router <Route/> component or by creating a Higher Order Component using withRouter.
  • Next, create the route you want to redirect to (I called mine to).
  • Third, call your redux action with both history and to.
  • Finally, when you want to redirect (e.g., when your redux action resolves), call history.push(to).

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.

查看更多
【Aperson】
5楼-- · 2019-01-30 06:49

I ended up creating a super simple middleware that roughtly looks like that:

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

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

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

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

So from there you just need to make sure that your successful actions have a redirect property. Also note, this middleware does not trigger next(). This is on purpose as a route transition should be the end of the action chain.

查看更多
登录 后发表回答
相关问题
查看全部
相关文章
查看全部
收藏的人(4)