React Router conditional routing component flashing/flickering

Viewed 1644

I'm trying to render some components only if the user is signed in. Whether the user is signed in is determined by current user object stored in a redux store. The problem is that the main App component, which contains the routes, does not receive the current user object to its' props instantly before its' first render call. Consequently, it redirects to Login page. After a second or two, the main App component finally receives the object into its' props and redirects where it should have in the first place.

I've tried to move the condition into the render method of the actual component, but still that component does not receive user object as props from redux store before its' first render, resulting in that same 'flash' of login page.

class App extends React.Component {
    constructor() {
        super();
    }

    unsubscribeFromAuth = null;

    async componentDidMount(): void {
        this.unsubscribeFromAuth = await auth.onAuthStateChanged(async userAuth => {
            if (userAuth) {
                await this.props.setCurrentUser(userAuth);
            }
        })
    }

    componentWillUnmount(): void {
        this.unsubscribeFromAuth();
    }

    render() {
        return (
            <div>
                <Navbar />
                <BrowserRouter history={history}>
                    <div>
                    <Switch>
                        <Route exact={true} path={'/register'} render={() => this.props.currentUser
                                                                            ? <Profile/>
                                                                            : <Register/>}/>
                        <Route exact={true} path={'/profile'} render={() => this.props.currentUser
                            ? <Profile/>
                            : <Login/>}/>
                        <Route exact={true} path={'/login'} render={() => this.props.currentUser
                                                                        ? <Events/>
                                                                        : <Login/>}/>
                        <Route exact={true} path={'/events'} render={() => this.props.currentUser
                                                                         ? <Events/> : <Login/>}/>
                        <Route exact={true} path={'/my_events'} render={() => this.props.currentUser
                                                                            ? <MyEvents/> : <Login/>}/>
                        <Route exact={true} path={'/new_event'} render={() => this.props.currentUser
                                                                            ? <NewEvent/> : <Login/>}/>
                        <Route exact={true} path={'/edit_event'} render={() => this.props.currentUser
                                                                        ? <EventEdit/>
                                                                        : <Login/>}/>
                        <Route exact={true} path={'/apply-to-event'} render={() => this.props.currentUser
                                                                                 ? <ApplyToEvent/>
                                                                                 : <Login/>}/>
                        <Route path={'/'} render={() => this.props.currentUser
                            ? <Events/>
                            : <Login/>}/>
                    </Switch>
                    </div>
                </BrowserRouter>
            </div>
        );
    }
}

const mapStateToProps = ({ user }) => ({
    currentUser: user.currentUser
});

const mapDispatchToProps = (dispatch) => ({
    setCurrentUser: async user => await dispatch(setCurrentUser(user))
});

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(App);

I would like to find a way how to render the component without the flashing/flickering of the Login component.

1 Answers

If i understood your code correctly, it appears me that problem is not router code but logic of redux auth state.

First of all; componentDidMount always work after first render. So you can never achieve what you want in componentDidMount. You will always miss first render. It will remember the auth state and won't need to fetch to server to know it.

If you wanna know auth info during first render, you should use localStorage or asyncStorage( forReact Native). So that you would know by the first render.

If you insist to not use localStorage; you might show some loading screen until you update the auth state. So react router won't have chance to route to login page and return back.

Related