4

My React container looks like this:

class App extends React.Component {
  componentDidMount() {       
    if (this.props.location && this.props.location.pathname != '/callback') {      
      userManager.getUser().then(response => {        
        if (!response || response.expired) {           
          userManager.signinRedirect();
        }  
        else{
          this.props.dispatch(userFound(response));
        }
      });
    }
  }
  render() {  
    return (
      <div>
        <Switch>          
          <PrivateRoute exact path="/" component={HomePage} user={this.props.user} />
          <PrivateRoute exact path="/dashboard" component={Dashboard} user={this.props.user} />
          <Route exact path="/callback" component={CallbackPage} />
          <Route component={NotFoundPage} />          
        </Switch>
      </div>
    );
  }
}

Callback component looks like this:

class CallbackPage extends React.Component {
  render() {
    // just redirect to '/' in both cases
    return (
      <CallbackComponent
        userManager={userManager}
        successCallback={() => this.props.dispatch(push("/"))}
        errorCallback={error => {
          this.props.dispatch(push("/"));
          console.error(error);
        }}
        >
        <div>Redirecting...</div>
      </CallbackComponent>
    );
  }
}

My Privateroute looks like this:

const PrivateRoute = ({ component: Component, user, ...rest }) => (  
  <Route {...rest} render={props => (
    user ? (
      <Component {...props} />
    ) : (
      <Redirect to={{
        pathname: '/notFoundPage',
        state: { from: props.location }
        }}
      />
    )    
  )} />
);

export default PrivateRoute; 

My store looks like:

export default function configureStore(initialState = {}, history) {

  const middlewares = [
    sagaMiddleware,
    routerMiddleware(history),
  ];
  const enhancers = [
    applyMiddleware(...middlewares),
  ];
  const composeEnhancers =
    process.env.NODE_ENV !== 'production' &&
    typeof window === 'object' &&
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
      ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
        // TODO Try to remove when `react-router-redux` is out of beta, LOCATION_CHANGE should not be fired more than once after hot reloading
        // Prevent recomputing reducers for `replaceReducer`
        shouldHotReload: false,
      })
      : compose;
  const store = createStore(
    createReducer(),
    fromJS(initialState),
    composeEnhancers(...enhancers)
  );
  store.runSaga = sagaMiddleware.run;
  store.injectedReducers = {}; 
  store.injectedSagas = {}; 
  loadUser(store, userManager);
  return store;
}

My issue is: the callback component is called twice. i can't find where it trigger from? first time, it goes to the success function as expected, then second time, it will go to the error function. Then page frozen and the URL shows in browser is callback link. I can't find why this callback is running twice? Can someone please help me with this. i hope you understood the issue.

this code is based on the redux-oidc example. Please click the following link. Redux-oidc example

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
Dayan
  • 711
  • 5
  • 16
  • 27

1 Answers1

2

Your router and redux store config looks fine. But you don't need to call getUser in your componentWillMount. Your app should be configured in such a way, that it only renders the routes when a valid user is present.

A possible solution would be to use render instead of componentWillMount like this:

render() {
  const routes = (
    <Switch>          
      <Route exact path="/" component={HomePage} user={this.props.user} />
      <Route exact path="/dashboard" component={Dashboard} user={this.props.user} />
      <Route exact path="/callback" component={CallbackPage} />
      <Route component={NotFoundPage} />          
    </Switch>
  );

  const loginPage = <LoginPage />; // <-- create a dedicated login page component where signinRedirect is called

  const isValidUserPresent = this.props.user && !this.props.user.expired;
  const isCallbackRouteRequested = this.props.location.pathname === '/callback';
  const shouldRenderRoutes = isValidUserPresent || isCallbackRouteRequested;

  return shouldRenderRoutes ? routes : loginPage;            
}
maxmantz
  • 702
  • 1
  • 10
  • 25
  • i have updated my store in the question you can have a look at it. And if i get rid of 'getUser' part from the code. i'm not doing signinRedirect. so should i use it like this?: if(!this.props.user && this.props.location && this.props.location.pathname != '/callback') { userManager.signinRedirect(); }. It still going to the callback component twice. first goes to the success function and it try to redirect to my home component but it doesn't go to the home component, instead it is coming back to the callback component and in my page i can only see 'Redirecting...' – Dayan May 27 '18 at 04:32
  • the only place i'm doing userManager.signinRedirect() is inside my componentDidMount, if i get rid of it. i'm not calling the server. And what i'm doing here is Automatic sign. – Dayan May 27 '18 at 04:37
  • I recommend binding the `signinRedirect` call to a login button which is displayed when there is no user present, as in `!this.props.user || this.props.user.expired`. Calling it automatically during `componentWillMount` has the potential to mess things up. – maxmantz May 27 '18 at 04:39
  • maxmantz, the thing is initially i have to call the identityserver, and do login there come back to the app. Another thing, i'm getting user details. after i retrieved user, it trying to do callback again – Dayan May 27 '18 at 04:42
  • Also I don't know what `` does. Use the normal `` instead. – maxmantz May 27 '18 at 05:18
  • i'm still having the issue. it still getting at callback page. – Dayan May 27 '18 at 05:42
  • i did this in my routes. now this works. not sure why? – Dayan May 27 '18 at 05:44
  • now second time when it comes to the routes, location path name is '/', but it goes inside callback route, that's the issue. now it redirects to the Homepage. temporary solution! – Dayan May 27 '18 at 05:47
  • 1
    This is not a viable solution! Please try to implement my approach and work from there. – maxmantz May 27 '18 at 05:56
  • maxmantz, i guess i found the solution. what i had to do was change my connect to **connect(mapStateToProps, mapDispatchToProps, null, { pure : false })** in my app component. I think it's working now, because app component is re-rendering when its parent components change. maybe earlier it was ignoring the rendering. Anyway, thanks for your help. – Dayan May 28 '18 at 05:26
  • 1
    Strange that this should be the solution. I honestly can't see how this change would solve the problem at hand. But if it works for you, I'm happy :-) – maxmantz May 28 '18 at 10:06