React Router and redux auth token double render

Viewed 395

So i am using redux, dispatching a token if user logs in. Then I fetch the user's token and if it exists I set X routes available other wise, Y routes. Problem is that at every refresh, the App component renders twice. The first time it token is null second time token is true.

Well, why is it rendering twice? Please answer with different approaches for the same action if you think its more efficient than that.

please, if you need more code, ask.

regards

code:

const App = () => {
    const dispatch = useDispatch();
    const token = useSelector((state) => state.auth.token);
    const onCheckAuth = useCallback(() => dispatch(checkAuth()), [dispatch]);

    useEffect(() => {
        onCheckAuth();
    }, [onCheckAuth]);

    const render = () => {
        if (token) {
            return (
                <Switch>
                    <Route path='/hearing' component={Hearing} />
                    <Route path='/logout' component={Logout} />
                    <Route path='/' exact component={Dashboard} />
                    <Redirect to='/' />
                </Switch>
            );
        }

        return (
            <Switch>
                <Route path='/login' component={Login} />
                <Redirect to='/login' />
            </Switch>
        );
    };

    console.log(token);

    return <>{render()}</>;
};

export default App;
2 Answers

First of all, I'll explain briefly why it is rendered twice.

When the app first loaded, the redux has initial state and the app dispatches checkAuth action. This is the first render.

Once the checkAuth is dispatched, the state of redux will be updated and it causes another render.

This why it is rendered twice.

Then I'll explain how to handle token in react App.

You should save token in Browser's local storage, not in the redux.

So when the app first loaded, token in local storage must be checked its existence and expiration. Then it's okay to save token to redux to be synced with token updates. If it's an invalid token, then you need to refresh a new token.

Finally using functional component you don't need to define render() method.

It should be returned directly.

const App = () => {
    const dispatch = useDispatch();
    const token = useSelector((state) => state.auth.token);
    const onCheckAuth = useCallback(() => dispatch(checkAuth()), [dispatch]);

    useEffect(() => {
        onCheckAuth();
    }, [onCheckAuth]);

    if (token) {
        return (
            <Switch>
                <Route path='/hearing' component={Hearing} />
                <Route path='/logout' component={Logout} />
                <Route path='/' exact component={Dashboard} />
                <Redirect to='/' />
            </Switch>
        );
    } else {
        return (
            <Switch>
                <Route path='/login' component={Login} />
                <Redirect to='/login' />
            </Switch>
        );
    }
};

export default App;

It must render twice, as after first render the useEffect fires and that itself fires onCheckAuth which does a dispatch, then as a result of dispatch the react/redux notifies with the new state, so the second render happens.

Related