简体   繁体   中英

Setting up private routes with react router

I am trying to setup a website with a login screen for unauthorized users and a dashboard for authorized users using react router dom.

Every time there is a route change (dashboard routes) when a user clicks a link in the sidebar, for example. The useEffect inside dashboard component is called which fetches data that I already have.


## ROUTES ##

export const appRoutes = auth => [
    {
        path: '/',
        component: () => auth ? <Redirect to='/dashboard' /> :<Login/>,
        exact: true
    },
    {
        path: '/dashboard',
        component: Guilds ## REDIRECTS TO THE NEXT ROUTE WITH ID ##,
        exact: true,
        private: true
    },
    {
        path: '/dashboard/:id',
        component: Dashboard,
        private: true
    },
    {
        path: '/dashboard/*',
        component: Dashboard,
        private: true
    }
]

export const dashboardRoutes = [
    {
        path: '/dashboard/:id',
        component: Home,
        exact: true
    }
]

## SIMPLIFIED APP COMPONENT ##

export default function App() {
    return (
        <ThemeProvider theme={theme}>
            <BrowserRouter>
                <Switch>
                    {appRoutes(auth).map(value => {
                        if(value.private) return <PrivateRoute path={value.path} component={value.component} exact={value.exact} key={value.path} auth={auth} />;
                        else return <Route path={value.path} component={value.component} exact={value.exact} key={value.path} />;
                    })}
                </Switch>
            </BrowserRouter>
        </ThemeProvider>
    )
}

## SIMPLIFIED DASHBOARD COMPONENT ## 

export default function Dashboard({ match }) {
    const [guild, setGuild] = useState(null);
    const [user, setUser] = useState(null);

    useEffect(() => {
        getGuild(match.params.id)
        .then(res => {
            setGuild(res.data);
            return getUser();
        })
        .then(res => {
            setUser(res.data);
        })
        .catch(err => {
            console.log(err);
        })
    }, [match.params.id]);

    return (
        <div className={classes.root}>
            <Header onToggleDrawer={onToggleDrawer} guild={guild} auth />
            <SideBar onToggleDrawer={onToggleDrawer} isOpen={drawerOpen} user={user} />
            <div className={classes.content}>
                <div className={classes.toolbar} />
                <div className={classes.contentContainer}>
                    {dashboardRoutes.map(value => {
                        return <Route exact={value.exact} path={value.path} component={value.component} key={value.path}/>
                    })}
                </div>
            </div>
        </div>
    )
}

## PRIVATE ROUTE COMPONENT ##

export const PrivateRoute = ({ component: Component, auth, ...rest }) => {
    return (
        <Route {...rest} render={(props) => (
            auth
                ? <Component {...props} />
                : <Redirect to='/' />
        )} />
    )
}

I'm not sure if I am approaching the situation correctly but any help would be great. I take it the function is called in-case a user comes to the site from a bookmark for example but if someone can shed some light that would be cool.

Thank you.

The reason behind that why the fetch is happening several times is the dependency array what you have for useEffect . I assume the match.params.id is changing when the user clicks then it changes the route which will trigger the fetch again.

Possible solutions:

1. Empty dependency array:

One possible solution can be if you would like to fetch only once your data is set the dependency array empty for useEffect . From the documentation :

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn't depend on any values from props or state, so it never needs to re-run.

So if you have the following, it will run only once:

useEffect(() => {
   // this part runs only once
}, []); // empty dependency array

2. Checking if the fetch happened already:

The other solution what I was thinking is to check if you have the value already in the guild variable just like below:

useEffect(() => {
    // no value presented for guild
    if (guild === null) {
       // code which is running the fetch part
    }
}, [match.params.id]);

I hope this gives you an idea and helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM