0

I'm trying to check if authState is true to show my side sidebar to the user. It works but I get an infinite loop.

here is the useEffect and useState

const [ authState, setAuthState ] = useState({
      username: "", 
      id: 0, 
      status: false
    });

useEffect(() => {
   axios.get('http://localhost:4000/users/auth/v', {withCredentials: true}).then((res) => {
        console.log(res.data);
        if (res.data.error) {
          setAuthState({ ...authState, status: false });
        } else {
          setAuthState({
            username: res.data.username, 
            id: res.data.id, 
            status: true
          });
          console.log(authState);
        }
      });

  },[authState]);

Edit: I am actually using a useContext

  return (
    <>
      <AuthContext.Provider value={{ authState, setAuthState }}>
        <Router>
        <Sidebar />
          <Switch>
            <Route path='/' exact component={Auth} />
            <Route path='/users/add' exact component={AddUser} />
            <Route path='/users' exact component={UserList} />
            <Route path='/users/:id' exact component={UserEdit} />
            <Route path='/roles/add' exact component={AddRole} />
            <Route path='/roles' exact component={RoleList} />
            <Route path='/roles/:roleId' exact component={EditRole} />
            <Route path='/logout' exact component={Logout} />
          </Switch>
        </Router>
      </AuthContext.Provider>
    </>
  );
}

Update

Using this code now but when I go into, like, lets says /users I need to refresh the page to let authState's status be recognized as true by the sidebar then the sidebar shows up

  useEffect(() => {
    axios.get('http://localhost:4000/users/auth/v', {withCredentials: true}).then((res) => {
      console.log(res.data);
      if (res.data.error) {
        setAuthState({ ...authState, status: false });
      } else {
        setAuthState({
          username: res.data.username, 
          id: res.data.id, 
          status: true
        });
      }
    });
  },[]);

here is my sidebar

function Sidebar() {

    const [ sidebar, setSidebar ] = useState(false);

    let userData = localStorage.getItem('user');
    // console.log(userData);

    const { authState } = useContext(AuthContext);
    

    const showSidebar = () => setSidebar(!sidebar)

    return (
        <>
        {authState.status &&
        <Styles>
            <Navbar bg="light">
            <Navbar.Brand>
                <NavIcon>
                    <MenuIcon onClick={showSidebar} />
                </NavIcon>
            </Navbar.Brand>
            <Navbar.Text className="user-options">
                <span className='user-name'>Hello, {userData}!</span> {/* WIP input user's fname and lname via context */}
                <span className='logout-link'><Link to="/logout">Logout</Link></span>
            </Navbar.Text>
            </Navbar>
            <Sidenav sidebar={sidebar}>
                <SidebarWrap>
                    <NavIcon>
                        <HighlightOffIcon onClick={showSidebar} />
                    </NavIcon>
                    <SidebarList>
                        {SidebarData.map((val, key) => {
                            return (
                                <li 
                                    key={key}
                                    className='row'

                                > 
                                    <Link 
                                    className='link-style' 
                                    to={val.link}
                                    id={window.location.pathname === val.link
                                        ? "active"
                                        : ""}
                                        >
                                        <SideIcon>{val.icon}</SideIcon>
                                        <SideTitle>{val.title}</SideTitle> 
                                    </Link>
                                </li>
                            )
                        })}
                    </SidebarList>
                </SidebarWrap>
            </Sidenav>
        </Styles>
        }
        </>
    )
}
madmeatballs
  • 73
  • 1
  • 10
  • 2
    You effect sets the auth state. But the auth state is a dependency, so it will trigger the effect again. Read the docs on what the dependency array in `useEffect` is for and how to use it. – Jayce444 Jun 11 '21 at 06:32
  • 1
    Just remove authState from useEffect dependancy array – Hamza Khursheed Jun 11 '21 at 06:37
  • Please have a look at [https://stackoverflow.com/questions/53715465/can-i-set-state-inside-a-useeffect-hook](https://stackoverflow.com/a/60867816/15863292) – Jonathan Weibel Jun 11 '21 at 09:45

2 Answers2

0

Since at the end of the useEffect you are changing the authState its calling the useEffect again. I would suggest having a flag to trigger check if the authState is updated and stop the loop, the code might be kinda wrong as i just wrote it here :P, you would have to set the updateAuth to true as well before calling the setAuthState. If you want it to run only at the start as Hamza Khursheed said, just remove the authState from the dependency array (the second argument of the use effect)

const [ authState, setAuthState ] = useState({
  username: "", 
  id: 0, 
  status: false
});
const [ updateAuth, setUpdateAuthState ] = useState(false)

 useEffect(() => {
  if(updateAuth){
    axios.get('http://localhost:4000/users/auth/v', {withCredentials: 
        true}).then((res) => {
       console.log(res.data);
    if (res.data.error) {
      setAuthState({ ...authState, status: false });
    } else {
      setAuthState({
        username: res.data.username, 
        id: res.data.id, 
        status: true
      });
      setUpdateAuthState(false);
      console.log(authState);
    }
  });
 }
 },[authState]);
  • You should get a warning like "`updateAuth` is not on `useEffect`'s deps list". Then, when you add it, you return to initial problem (you set a state that is triggering an `useEffect` that generates an infinite loop). Your workaround works only if you define `updateAuth` as `useRef()` (but stills a workaround). – Giovanni Esposito Jun 11 '21 at 06:59
  • Good point, I think it can be just ignored, or just added to the dependency list, its going to be called a second time but and stop executing the useEffect at the updateAuth flag, since neither updateAuth or authState are updated if updateAuth is false then the loops stops and we get no warnings. – Alex Ioannou Jun 11 '21 at 07:06
  • In your code `axios.get` will be never executed because `updateAuth`'s initial state is set to `false`. Anyway, even if with this workaround you avoid second `axios.get` call, you don't avoid that `useEffect` verifies the if condition again and again and again... And, a suggestion, never ignore React's warning. – Giovanni Esposito Jun 11 '21 at 07:16
0

You should adjust your dependency array to reflect on what change you want your useEffect to fire. If you want to get authorization state on route change this should work:

React.useEffect(() => {
    axios
        .get('http://localhost:4000/users/auth/v', { withCredentials: true })
        .then((res) => {
            console.log(res.data);
            if (res.data.error) {
                setAuthState({ ...authState, status: false });
            } else {
                setAuthState({
                    username: res.data.username,
                    id: res.data.id,
                    status: true,
                });
                console.log(authState);
            }
        });
}, [window.location.pathname]);
Simas.B
  • 724
  • 1
  • 13
  • 31