Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite loop on nested routes #84

Closed
davidglbr opened this issue Oct 11, 2016 · 10 comments
Closed

Infinite loop on nested routes #84

davidglbr opened this issue Oct 11, 2016 · 10 comments

Comments

@davidglbr
Copy link

davidglbr commented Oct 11, 2016

Hey @mjrussell ,
Thank you for this great package.

I'm having a problem of infinite reload on a nested route. I've looked online and tried learning from Issue 44 here but I'm not able to find the root cause.

This is a simplified version of my routes:

"use strict";

import React from 'react';
import { Route, IndexRoute } from 'react-router';

import Login from './components/Login';
import AppContainer from './components/AppContainer';
import HomePageContainer from './components/HomePageContainer';
import PostItem from './components/inspiration/Post';
import NotFoundPage from './components/NotFoundPage';
import Admin from './components/Admin';
import {ValidateUser,  ValidateAdmin, ValidateNoAuth} from './auth/validate';

const ValidateUserAuthTree = ValidateUser(({children, ...moreProps}) => children && React.cloneElement(children, moreProps));

export default (
    <Route>
        <Route path="login" component={ValidateNoAuth(Login)}/>
        <Route component={ValidateUserAuthTree}>
            <Route path="/" component={AppContainer}>
                <IndexRoute component={HomePageContainer}>
                    <Route path="post" component={PostItem}/>
                </IndexRoute>
            </Route>
        </Route>
        <Route path="/admin" component={ValidateAdmin(Admin)}/>
        <Route path="*" component={NotFoundPage}/>
    </Route>
);

Wrapper definitions look like this:

"use strict";

import { UserAuthWrapper } from 'redux-auth-wrapper';
import { routerActions } from 'react-router-redux';

const authConfig = {
    authSelector:               state => state.auth.user,
    authenticatingSelector:     state => state.auth.authenticating,
    redirectQueryParamName:     'redirect',
    redirectAction:             routerActions.replace,

};

export const ValidateUser = UserAuthWrapper({
    wrapperDisplayName:     'ValidateUser',
    predicate:              authData => !!authData,
    failureRedirectPath:    '/login',
    allowRedirectBack:      true,
    ...authConfig
});

export const ValidateAdmin = UserAuthWrapper({
    wrapperDisplayName:     'ValidateAdmin',
    predicate:              authData => authData && authData.isAdmin,
    failureRedirectPath:    '/',
    allowRedirectBack:      false,
    ...authConfig
});

export const ValidateNoAuth = UserAuthWrapper({
    wrapperDisplayName:     'ValidateNoAuth',
    predicate:              authData => !authData,
    failureRedirectPath:    (state, ownProps) => ownProps.location.query.redirect || '/',
    allowRedirectBack:      false,
    ...authConfig
});

export const AdminOnlyLink = UserAuthWrapper({
    authSelector:       state => state.auth.user,
    wrapperDisplayName: 'AdminOnlyLink',
    predicate:          authData => !!authData,
    FailureComponent:   null
});

Basically all components on the root route gets into an infinite rendering loop. I think that the wrapper is re-rendered because its child is re-rendered but can't break this cycle.
The /admin route renders fine and so is the /login. I've also tried removing inner routes from the root route and nothing helps to break the rendering loop. I've also tried wrapping the root route with other wrappers (such as the admin or login wrappers) and that didn't help as well. All of this leads me to conclude that something with the nested route is wrong.

I've applied the ValidateUserAuthTree at the root level based on the documentation. What else can I do?

Would greatly appreciate your help.

@mjrussell
Copy link
Owner

@davidglbr Do you even need the nested route in this way? The nesting was more for users who had a flat route structure and needed to wrap up "sibling" routes with the auth checks. Seems like you could do:

            <Route path="/" component={ValidateUser(AppContainer)}>
                <IndexRoute component={HomePageContainer}>
                    <Route path="post" component={PostItem}/>
                </IndexRoute>
            </Route>
        </Route>

Does it still have the same behavior with this alternate routing structure?

@davidglbr
Copy link
Author

@mjrussell thank you for the prompt response!

The nested route was definitely redundant. I did exactly what you suggested but I'm still experiencing the infinite rendering loop with the alternate routing structure.

Any idea?

@mjrussell
Copy link
Owner

@davidglbr if only it was so easy! Well at least thats one less thing to consider...

Could you post your auth reducer and try temporarily disabling your Login HOC (ValidateNoAuth)? Im wondering if you are looping between /login and your protected routes.

@davidglbr
Copy link
Author

@mjrussell thank you for sticking in. Truly appreciate your help.

My auth reducer is:

import { LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE, LOG_OUT } from '../constants/actionTypes';
import clone from 'clone';
import initialState from './initialState';


const authReducer = (state = initialState.auth, action) => {
    let newState = clone(state);

    switch (action.type) {
        case LOGIN_REQUEST:
            newState.authenticating = true;
            newState.user = null;
            newState.errorMessage = null;
            break;

        case LOGIN_SUCCESS:
            newState.authenticating = false;
            newState.user = action.user;
            newState.errorMessage = null;
            break;

        case LOGIN_FAILURE:
            newState.authenticating = false;
            newState.errorMessage = action.message;
            break;

        case LOG_OUT:
            newState.authenticating = false;
            newState.user = null;
            newState.errorMessage = null;
            break;

        default:
            return newState;
    }
    return newState;
};

export default authReducer;

and my auth actions are:

import * as types from '../constants/actionTypes';


export const requestLogin = () => ({
    type: types.LOGIN_REQUEST
});


export const receiveLogin = user => ({
    type: types.LOGIN_SUCCESS,
    user: user
});


export const loginError = message => ({
    type: types.LOGIN_FAILURE,
    message: message
});


export const logout = () => ({
    type: types.LOG_OUT
});

Disabled the login HOC so that login route is:

        <Route path="login" component={Login}/>

but it didn't change the problem.
Other things that didn't help:

  1. Setting allowRedirectBack of ValidateUser to false.
  2. Setting predicate of ValidateUser to () => true

Don't know where to go from here... any other things I can share that may help?

@davidglbr
Copy link
Author

Just to close the picture, the relevant part of initialState is:

export default {
    auth: {
        user: null,
        authenticating: false,
        errorMessage: null
    }
}

@mjrussell
Copy link
Owner

Happy to dig through this with you...nothing jumping out immediately to me but I'll keep looking. Obvious a repo is the ideal case for debugging this but its not always feasible.

In the original description you mentioned that these were simplified routes. How did you simplify it when you posted them? Are you using getComponent at all in your routes? I've seen some odd behavior with dynamic routing and HOCs.

@davidglbr
Copy link
Author

davidglbr commented Oct 11, 2016

@mjrussell thanks again. Can't say this enough!
Can't share the full code but will sit down to prepare a stripped down version (no "content", just the web app) if we can't find anything else.

Not using dynamic routing. Full routes are:

    <Route>
        <Route path="login" component={ValidateNoAuth(Login)}/>
        <Route path="/" component={ValidateUser(AppContainer)}>
            <IndexRoute component={HomePageContainer}>
                <Route path="post" component={PostItem}/>
                <Route path="view/:hash" component={ViewItem}/>
            </IndexRoute>
            <Route path="class" component={ClassContainer}>
                <Route path="content/:class_name" component={ClassContent}/>
                <Route path=":class_name" component={ClassLobby}/>
            </Route>
            <Route path="account" component={Account}/>
        </Route>
        <Route path="/admin" component={ValidateAdmin(Admin)}/>
        <Route path="*" component={NotFoundPage}/>
    </Route>

(This is the only route file on this react app)

I did a full project search to make sure I'm not using getComponent and found a few usages of it in the distribution code that is built by webpack.
Wondering why this happens. Any chance the dynamic routing is somehow brought in when the JSX routes above are transpired to JS code? I'm not familiar with dynamic routing so can't think of anything else. Any ideas on how I can eliminate that / check further?

@mjrussell
Copy link
Owner

@davidglbr can we try moving this to Gitter or Discord while working through a few of these. Will definitely want to recap for other users afterwards if they run into similar issues but that might easier for back and forth stuff.

@davidglbr
Copy link
Author

Sure thing.
Reaching out on Gitter and I'll recap here after the issue is found!

@davidglbr
Copy link
Author

After great help from @mjrussell I was able to figure out that ComponentWillUpdate was called and triggered my own code that in turn kept changing the component and thereby ComponentWillUpdate kept calling itself. We're not sure why redux-auth-wrapper triggered this, but this seems like a unique combination of my code and not an issue with the package. We will update here if we find something about it.

Closing the issue.
Thank you so much @mjrussell!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants