0

I'm trying to figure out how to set my user state with react hooks after an axois post request. Struggling to find a good solution. I've read the react hooks documentation and almost all examples that I find are just when the page loads. I want to setState on form submission as the user logs in.

Login.js

   const handleLogin = (evt) => {
    //Prevent default form submission
    evt.preventDefault();

    const loginDetails = {
        username: username,
        password: password,
    }
    //send login request to api
    axios.post('http://localhost:5000/users/login', loginDetails)
    .then((res) => {
      setUser(loginDetails.username);
      history.push('/');
    })
    .catch((err) => {
      console.log('incorrect password/user');
      history.push('/login');
    })
}

Due to the Asynchronous behaviour the state is not being set before the redirect. I have also tried to place the redirect in a useEffect, but it constantly reloads the page, even when I past in a dependency. How to I ensure that my state is being set, passed into my navbar component as a prop before the history.push('/') takes effect.

Edit

App.js

  return (

    <Router history={history} >
      <div className={styles.app}>
          <Navbar user={user}/>
          <Switch>
            <Route exact path="/" component={Home}/>
            <Route path="/about" component={About}/>
            <Route exact path="/blog" component={Blog}/>
            <Route exact path="/blog/create" component={CreateBlog}/>
            <Route exact path="/blog/:id" component={ViewBlog}/>
            <Route path="/demo" component={Demo}/>
            <Route path='/resume' component={Resume}/>
            <Route 
            exact path="/login" 
            render={(props) => (
              <Login loginHandler={handleLogin}
                     usernameHandler={onChangeUsername}
                     passwordHandler={onChangePassword}/>)}
              />
            <Route exact path="/register" render={(props) => (
              <Register usernameHandler={onChangeUsername}
                        passwordHandler={onChangePassword}
                        handleRegister={handleRegister}/>
            )}/>
            
              
          </Switch>
          <Footer/>
      </div>
      
    </Router>
  );
}

export default App;
rwright94
  • 45
  • 1
  • 7
  • can you post your render method. – Arun205 Jan 05 '21 at 02:51
  • Edit posted with the render/return from App.js – rwright94 Jan 05 '21 at 02:53
  • redux is the answer :) eg: https://jasonwatmore.com/post/2020/03/02/react-hooks-redux-user-registration-and-login-tutorial-example – Jehad Ahmad Jaghoub Jan 05 '21 at 02:56
  • You should use setState in useEffect conditionally. If loginDetails.username != null setState(loginDetails.username).Ideally, state management libraries like redux is the better solution. – Arun205 Jan 05 '21 at 03:01
  • `Due to the Asynchronous behaviour the state is not being set before the redirect.` - this usually won't cause too much delays though, it's almost unnoticeable delay. Even if you use redux, it will still take time for state to be updated – Janice Zhong Jan 05 '21 at 03:07
  • @ Jehad Ahmad Jaghoub I've looked at creating a new simple project to learn redux which I plan on doing. Looking to understand React more before I implement a library. @Arun205 If I useEffect conditionally, where would I implement it. React advises against calling useEffect within a function. – rwright94 Jan 05 '21 at 03:14
  • @rwright94 refer this https://stackoverflow.com/questions/54954091/how-to-use-callback-with-usestate-hook-in-react – sathish kumar Jan 05 '21 at 03:27
  • 1
    Using `useEffect` as @Arun205 has suggested is the way to go. However, the condition you check is `loginDetails.username` and if it isn't null, then `history.push('/')`. You'll have to add `loginDetails.username` as a dependency to useEffect. About the other comment about redux being the answer, it is not the answer. It just makes user auth easier because the auth state can be exposed to any component. Lastly, you should have a read on using [private routes](https://dev.to/ibrahimawadhamid/how-to-create-a-private-route-in-react-route-guard-example-for-authenticated-users-only-kin). – Scratch'N'Purr Jan 05 '21 at 03:29
  • The issue with using the `useEffect` is that `loginDetails` is not a `state variable`, its a const created within the `handleLogin` function to pass with axios. `useEffect` does not know about the `loginDetails`. My problem is getting the information to persist after the push. I already have a `useEffect` dependency attached to the `user` object so that when it changes with setUser it calls `useEffect` to log the data. If I remove the history push it saves that data just fine and logs it to the console. `useEffect` won't work with a if statement as it causes an infinite page reload. – rwright94 Jan 05 '21 at 04:29
  • Would `const history = createBrowserHistory({forceRefresh:true});` cause problems with setting state? – rwright94 Jan 05 '21 at 04:41
  • @rwright94 I'm not sure about the `forceRefresh`, but if `loginDetails` is not a state variable, then use the `user` state variable which you can pass down as a prop to the component. Then in your `useEffect`, you check the truthy value of `user` and `history.push('/')` if it is true. Then add `user` as a dependency. There should not be infinite page reloads if `user` doesn't change (hence the dependency). – Scratch'N'Purr Jan 05 '21 at 11:14

0 Answers0