1

I'm building a React application and the Header component has a Menu button that toggles the sidebar and makes a logout. In other words, it only needs to appear if the user is logged in.

I'm generating a JWT in the backend and storing it in localStorage so an auth() method can get it and decide if user is logged in. The problem is, when I login, the Header doesn't appear. If I refresh the page, it appears. And if I do the opposite, which means logging out, I get redirected to login page but the Header is still there. If I refresh the page, it stops showing itself.

I set a console.log() in the auth method and I realized it doesn't react to trigger properly.

I'm using Ant Design and the app structure is

App.tsx
   |
   | - views
   |     |
   |     | -ListProducts accessed by "/" route and only available when logged in
   |     | -Login accessed by "/login" route
   |
   | - components
   |     |
   |     | - Routes.tsx
   |
   | - routines
   |     |
   |     | - auth.ts

App.tsx

import React, { FunctionComponent, useEffect } from "react";
import './assets/styles.css';
import Routes from "./components/Routes";
import { BrowserRouter, Link } from "react-router-dom";
import { Drawer, List, PageHeader, Button } from "antd";
import { MenuOutlined, PoweroffOutlined } from "@ant-design/icons";
import auth from "./routines/auth";

const App: FunctionComponent = () => {

const [isDrawerVisible, setIsDrawerVisible] = React.useState<boolean>(false);
const routes: {id: number, name: string, route: string}[] = [
    {id: 1, name: "Home", route: "/"},
    {id: 2, name: "Add User", route: "/add-user"}
]

return (
    <BrowserRouter>
        {!auth() && <PageHeader
            className = "site-page-header"
            title = {
                <Button
                    type = "text"
                    shape = "circle"
                    icon = {<MenuOutlined />}
                    size = "large"
                    onClick = {() => setIsDrawerVisible(true)}
                />}
            extra = {[
                <Link key = "1" to = "/logout"><Button danger icon = {<PoweroffOutlined />}>Logout</Button></Link>
            ]}
        />}
        <Drawer
            title = "Hello, 'User'!"
            placement = "left"
            closable
            onClose = {() => setIsDrawerVisible(false)}
            visible = {isDrawerVisible}
            key = "left"
        >
            <List
                itemLayout="horizontal"
                dataSource={routes}
                renderItem={item => (
                  <List.Item>
                    <List.Item.Meta
                      title={<Link to = {item.route} onClick = {() => setIsDrawerVisible(false)} style = {{display: "block"}}>{item.name}</Link>}
                    />
                  </List.Item>
                )}
            />
        </Drawer>

        <Routes />
    </BrowserRouter>
)
}

export default App;

Routes.tsx

import React, { FunctionComponent } from "react";
import { Switch, Route, Redirect } from "react-router-dom";
import ListProducts from "../views/ListProducts";
import Login from "../views/Login";
import AddUser from "../views/AddUser";
import auth from "../routines/auth";
import Logout from "../views/Logout";
import AddProduct from "../views/AddProduct";

const PrivateRoute = ({ component: Component, ...rest}: any) => (
<Route
    {...rest}
    render = {props => !auth() ? (
        <Component {...props} />
    ) : (
        <Redirect to = {{pathname: "/login", state: {from: props.location}}} />
    )}
/>
)

const Routes: FunctionComponent = () => {
    return (
        <Switch>
            <Route path = "/login" component = {Login} />
            <PrivateRoute exact path = "/" component = {ListProducts} />
            <PrivateRoute path = "/add-product" component = {AddProduct} />
            <PrivateRoute path = "/add-user" component = {AddUser} />
            <Route path = "/logout" component = {Logout} />
        </Switch>
        )
    }
export default Routes;

auth

const auth = () => {
    const conditionals: boolean[] = [
        localStorage.getItem("token") !== undefined,
        localStorage.getItem("token") !== "",
        localStorage.getItem("token") !== null
    ]
    console.log(conditionals.includes(false); // This command isn't trigger when logging in and logging out
    return conditionals.includes(false);
}

export default auth;

Raphael Alvarenga
  • 868
  • 1
  • 7
  • 13

1 Answers1

1

When you change the route, say from ListProducts to logout screen, your <App /> component doesn't get re-rendered. Only the child components, i.e. ListProducts and Logout are affected by this action.

Since App component doesn't get re-rendered on route change, your logic for showing/hiding the header component in App component is executed only once (i.e. on first launch) and it retains that state the entire session.

You can refer here on how to handle your header menu component - React: Hide a Component on a specific Route

Hope this helps.

  • Hello, Arpitha and thanks for answering. I checked the link you passed and got the idea, however there is one problem: the PageHeader needs its parent props to open/close the sidenav. If I use withRouter and pass router as props, then in this case I apparently can't have the parent props to control the sidenav. – Raphael Alvarenga Jul 08 '20 at 16:22