0
votes

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.

1

1 Answers

0
votes

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!