1

The function below is meant to check if user is authorized before rendering a route with react-router. I got the basic code from somewhere I forgot.

This was working fine when I had a simple synchronous check to local storage, but when I introduced an Axios call to the server things got messy.

I read a lot of the SO's questions on like issues, ie, promises not returning value and I seem to have modified my code to conform to the regular pitfalls.

I particularly used the last answer to this question to fix my setup, but the issue remains.

On the code below, the console.logs output the parameters correctly, meaning that the failure with related to the return statement.

The specific error is:

Error: AuthRoute(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

import React from 'react';
import PropTypes from 'prop-types';
import { Redirect, Route } from 'react-router-dom';
import axios from 'axios';

const PRIVATE_ROOT = '/account';
const PUBLIC_ROOT = '/';

const RenderRoute = ({response, isPrivate, Route, Redirect, component, ...props}) => {
    console.log('is_logged', response.data.is_logged); // boolean
    console.log('isPrivate', isPrivate); // boolean
    console.log('response', response); // axios object
    console.log('route', Route); // function route
    console.log('component', component); // home component
    console.log('props', {...props}); // route props
    if (response.data.is_logged) {
        // set last activity on local storage
        let now = new Date();
        now = parseInt(now.getTime() / 1000);
        localStorage.setItem('last_active', now );

        return isPrivate
        ? <Route { ...props } component={ component } />
        : <Route { ...props } component={ component } />;
    } else {
        return isPrivate
        ? <Redirect to={ PUBLIC_ROOT } />
        : <Route { ...props } component={ component } />;
    }
}

const AuthRoute = ({component, ...props}) => {
    const { isPrivate } = component;
    let last_active_client = localStorage.getItem('last_active') ? localStorage.getItem('last_active') : 0;
    let data = {
    last_active_client: last_active_client
    }
    let getApiSession = new Promise((resolve) => {
        resolve(axios.post('/api-session', data));
    });
    getApiSession.then(response => RenderRoute({response, isPrivate,Route, Redirect, component, ...props})
).catch((error) => {
        console.log(error);
 });
 }


export default AuthRoute;
BernardA
  • 1,391
  • 19
  • 48
  • Random guess, but do you need to return the promise in `AuthRoute`? `return getApiSession.then...` Also, there's not much point in a promise that resolves immediately - just chain off of `axios.post`. – CertainPerformance May 12 '18 at 09:08
  • @CertainPerformance. That was a good guess. It solves that issue and if you place a proper answer I will accept it. Unfortunately it now throws another error: Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead. Any ideas on that one? Not sure I understand your point about promise that resolves immediately. It will go to the server to fetch the info. – BernardA May 12 '18 at 09:13
  • You need a Loading page/route. Return it in AuthRoute. Then when you have `getApiSession` response, go to next route. – dumitru May 12 '18 at 09:21

1 Answers1

0

This is what worked for me after comments above. It was necessary to create a separate component class. It's not tremendously elegant, but works.

The code needs to be placed into componentWillReceiveProps() for it to update at each new props. I know componentWillReceiveProps() is being deprecated. I will handle that separately.

/auth/auth.js

import React from 'react';
import RenderRoute from './components/render_route';


const AuthRoute = ({component, ...props}) => {
    let propsAll = {...props}
    return (
        <RenderRoute info={component} propsAll={propsAll} />
    )
}

export default AuthRoute;

/auth/components/render_route.js

import React from 'react';
import PropTypes from 'prop-types';
import { Redirect, Route } from 'react-router-dom';
import axios from 'axios';


const PRIVATE_ROOT = '/account';
const PUBLIC_ROOT = '/';

class RenderRoute extends React.Component {
constructor (props) {
    super(props);
    this.state = {
        route: ''
    }
}

componentWillReceiveProps(nextProps, prevState){
    const { isPrivate } = this.props.info;
    let last_active_client = localStorage.getItem('last_active') ? localStorage.getItem('last_active') : 0;
    let data = {
        last_active_client: last_active_client
    }
    axios.post('/api-session', data).then(response => {
        let isLogged = response.data.is_logged;

        if(response.data.is_logged) {

            // set last activity on local storage
            let now = new Date();
            now = parseInt(now.getTime() / 1000);
            localStorage.setItem('last_active', now );
            this.setState({ route: isPrivate
                ? <Route { ...this.props.propsAll } component={ this.props.info } />
                : <Route { ...this.props.propsAll } component={ this.props.info } />
            })
        } else {
            this.setState({ route: isPrivate
                ? <Redirect to={ PUBLIC_ROOT } />
                : <Route { ...this.props.propsAll } component={ this.props.info } />
            })
        } 
    }).catch((error) => {
        console.log(error);
    });
}

render() {
    return (
    <div>
        {this.state.route}
    </div>
    )
}

}

export default RenderRoute;
BernardA
  • 1,391
  • 19
  • 48