简体   繁体   中英

Why is my React Authentication component using AWS Amplify being rendered infinitely when using React Router V6 to protect routes

I am trying to create my own custom authentication using React, AWS Amplify, and React Router V6, and my goal is to protect certain routes so users that are not logged in can't access them.

My code is here:

import './App.css';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import Navbar from './components/Navbar/Navbar';
import Dashboard from './components/Dashboard/Dashboard';
import Reports from './components/Reports/Reports';
import Patients from './components/Patients/Patients';
import { BrowserRouter, Navigate, Outlet, Route, Routes, useLocation, } from 'react-router-dom';
import React, { useEffect, useState } from 'react';
import '@aws-amplify/ui-react/styles.css';
import Signup from './components/Signup/Signup';
import Signin from './components/Signin/Signin';
import { Auth } from 'aws-amplify';

const darkTheme = createTheme({
  palette: {
    mode: 'dark',
  }
});

const useAuth = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const fetchUser = async () => {
      const response = await Auth.currentAuthenticatedUser();
      setUser(response);
    }
    fetchUser();
  })

  return { user };
}

function RequireAuth() {
  const auth = useAuth();
  const location = useLocation();
  console.log(auth);

  return (
    auth
      ? <Outlet/>
      : <Navigate to='/signin' state={{ from: location}} replace/>
  );
}

const App = () => {

  return (
    <ThemeProvider theme={darkTheme}>
      <BrowserRouter>
        <Routes>
          <Route element={<RequireAuth />}>
            <Route path="/" element={<Navbar />}>
              <Route path="dashboard" element={<Dashboard />} />
              <Route path="patients" element={<Patients />} />
              <Route path="reports" element={<Reports />} />
            </Route>
          </Route>
          <Route path="/signin" element={<Signin />} />
          <Route path="/signup" element={<Signup />} />
        </Routes>
      </BrowserRouter>
    </ThemeProvider>
  );
}

export default App;

I have spent the whole day trying countless methods but I keep getting the same results. What I'm trying to do is protect 3 routes Dashboard, Patients, and Reports. Whenever I click the sign-in button, I get around 500 logs of the current user as shown below:

在此处输入图像描述

Does anyone know what is triggering this component to re-render infinitely (hence executing my console.log(user) function), and is there any solution to fix this?

It appears the useEffect hook is missing a dependency array, so its callback is triggered each render cycle. Since the effect updates state, it triggers a rerender.

Add a dependency array.

useEffect(() => {
  const fetchUser = async () => {
    const response = await Auth.currentAuthenticatedUser();
    setUser(response);
  }
  fetchUser();
}, []);

If you need to run this effect more than once when the routes are mounted, then you may need to add a dependency, like location if/when the route path changes.

Example:

const { pathname } = useLocation();

useEffect(() => {
  const fetchUser = async () => {
    const response = await Auth.currentAuthenticatedUser();
    setUser(response);
  }
  fetchUser();
}, [pathname]);

Since the useEffect hook runs at the end of the initial render cycle you may want to also conditionally wait to render the outlet or redirect until the user state is populated.

Example:

const useAuth = () => {
  const [user, setUser] = useState(); // <-- initially undefined

  useEffect(() => {
    const fetchUser = async () => {
      const response = await Auth.currentAuthenticatedUser();
      setUser(response);
    }
    fetchUser();
  }, []);

  return { user };
}

...

function RequireAuth() {
  const auth = useAuth();
  const location = useLocation();

  if (auth === undefined) {
    return null; // or loading indicator, etc...
  }

  return (
    auth
      ? <Outlet/>
      : <Navigate to='/signin' state={{ from: location }} replace/>
  );
}

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