angularjsreactjsangular-ui-routerreact-routerangular-ui-router-extras

Automatic Deep Sub-Route Redirecting in React-Router


I have recently been transitioning a project from AngularJS + UI-Router+ UI-Router-Extras to React + React-Router.

One of the better features in UI-Router-Extras that I'd like to bring to React-Router is called
Deep State Redirect. In this feature, when navigating to a route which has subroutes, the application knows to redirect the user to the last subroute of it that was visited, or if none of its subroutes have yet been visited then it redirects to its first subroute to have been registered.

So for example if the loaded routing tree looks like this:

/main  
  |_/main_sub_1  
  |_/main_sub_2
/secondary

and the user starts at route /main/main_sub_2, then goes to /secondary, then goes to /main, they will be automatically redirected to /main/main_sub_2 since /main/main_sub_2 is the last subroute of /main to have been visited.

I know that I could implement this in react router by using
<IndexRedirect to={getLastSubRoute(parentRoute)}> where parentRoute is the full path of the parent <Route> tag, and getLastSubRoute is self-explanitory, but the problem with this is that I would need to add such an <IndexRedirect> tag to every single route I create, which is not optimal since the routes are loaded dynamically, there may be up to 100 subroutes, and much of the application's routing will be written by other people who I shouldn't be relying on to remember to add that tag under every <Route> tag they write.

Ideally, I should be able to apply some function or mixin to the base <Router> tag in the React routing definition to add this functionality to all routing underneath it, but I'm not sure where to start. How might I solve this problem?


Solution

  • Your best bet and possibly the simplest solution would be to set an onChange hook on one of the top level routes. The hook would get called with the next parameter, which would be the next route that the user would be going to.

    You would also have the hierarchical structure of routes there (navigating through to parent and children of the parent), so you could dynamically redirect using the replace function, that gets passed in as a parameter also.

    I implemented something similar for permission and role management. What I also did was to .bind my store to the function that I pass into the route hook. You could possibly store the route you'd like to redirect to on the user in the state tree. Basically what you refer to as getLastSubRoute.

    ...
    <Route onChange={myRedirectFunctionThatHasStoreBound} .. >
       ... // other routes
    </Route>
    ...
    
    
    function myRedirectFunctionThatHasStoreBound(store, prev, next, replace, callback) {
        const user = store.getState().user;
        const redirectTo = getLastSubRouteForRoute(user, next);
    
        if (redirectTo) {
            replace(redirectTo);
        }
    
        // don't forget this is you list callback as a param
        // your app might stop working, explanation below
        callback(); 
    }
    

    If callback is listed as a 4th argument, this hook will run asynchronously, and the transition will block until callback is called.

    EDIT: Keep in mind that this will only work if you are using react-router that's newer than or equal to in version to react-router 2.1