Connect react-router-dom with login page

110 views Asked by At

I want to connect react-router-dom with my login page. But it doesn't work how I want. Attribute "to" of Link element refresh after I click button. I want to change this attribute after button clicked.

Everything works fine. When I type correct email and password, conditions work, console log correct messages. When I click again button, Link works well and moves me to /home (there is my home page).

  const loggedUser = Users.filter(({ email }) => email === emailInput);
  const [linkTo, setLinkTo] = useState('/');
  const [showLink, setShowLink] = useState(false);

  const handleLoginButton = () => {
    const password = loggedUser.map(({ password }) => password)[0];
    console.log(password);

    if (!loggedUser) {
      console.log('Error in your email');
    } else {
      console.log('Correct email');

      if (passwordInput === password) {
        console.log('and correct password!');
        setLinkTo('/home');
        setShowLink(true);

        console.log(linkTo);
      } else {
        console.log('but wroing password');
        setLinkTo('/');
        setShowLink(false);
        console.log(linkTo);
      }
    }
  };

Button:

<Link to={linkTo}>
  <LoginButton onClick={handleLoginButton}>Login</LoginButton>
</Link>

What is another solution to this problem?

3

There are 3 answers

0
Khant Wai Kyaw On

My suggestion is trying to understand where to use HTML Tags first. By which case, HTML hyerlink and button, as we all know they have their own attributes and different event handler methods. You should have to use polymorphic component if you were to use button as a link or link as button. And Nothing is make-sense to use link in form submit.

But I'll share you a recommended note that might be a good help to you.

import { useNavigate } from 'react-router-dom';

const LoginForm = () => {
  const navigate = useNavigate();

  const handleSubmit = () => {
      navigate('route'); // Add your route name
  }

  return (
     <form>
       <Input label="Username" placeholder="Username" />
       <Input label="Passowrd" placeholder="Password" type="password" />
       <Button onClick={handleSubmit}>Submit</Button>
     </form>
   );
}


My recommended way

const LoginForm = () => {
  const navigate = useNavigate();

  const handleSubmit = (data) => {
      console.log(data);
      navigate(route); // Add your route name
  }

  return (
     <form onSubmit={handleSubmit}>
       ...
       <Button type="submit">Submit</Button> 
     </form>
   );
}

// Note : type Props is optional. The button inside form is automatically type submit.

Additional Suggestions

Handling form in React.js, manually, is a little complex in which validation, re-rendering and something more.

It's better to use third-party library like react-hook-form, formik and mantine-form.

Validtion with Zod is a bonus point.

0
Rohit Singh On
// LoginPage.js
import React from 'react';

const LoginPage = () => {
  return (
    <div>
      <h2>Login Page</h2>
      {/* Your login form and logic go here */}
    </div>
  );
};

export default LoginPage;

// App.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
import LoginPage from './LoginPage';

const App = () => {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/login">Login</Link>
            </li>
          </ul>
        </nav>

        <hr />

        <Switch>
          <Route path="/login" component={LoginPage} />
          {/* Add more routes as needed */}
        </Switch>
      </div>
    </Router>
  );
};

export default App;
0
Drew Reese On

Issue

Ignoring the UI/UX anti-pattern that is wrapping an interactive element around another interactive element, the issue here is that the button's click event propagates to the link component and immediately invokes the link's default action, e.g. it navigates to the specified target path, i.e. the value of linkTo. Then the handleLoginButton callback is processed and enqueues any local React state updates.

Solution

From the UI code and description you have, you don't want to immediately navigate. There are a few options:

  1. Stop the propagation of the button element's onClick event. You'll need to manually navigate to the target path.

    import { Link, useNavigate } from 'react-router-dom';
    
    ...
    
    const navigate = useNavigate();
    
    ...
    
    const handleLoginButton = (e) => {
      e.stopPropagation();
    
      const password = loggedUser.map(({ password }) => password)[0];
      console.log(password);
    
      if (!loggedUser) {
        console.log('Error in your email');
      } else {
        console.log('Correct email');
    
        if (passwordInput === password) {
          console.log('and correct password!');
          navigate("/home", { replace: true });
        } else {
          console.log('but wrong password');
          navigate("/", { replace: true });
        }
      }
    };
    
    ...
    
    <Link to={linkTo}>
      <LoginButton onClick={handleLoginButton}>
        Log In
      </LoginButton>
    </Link>
    
  2. Move the onClick={handleLoginButton} to the Link component and prevent the default link action. You'll need to manually navigate to the target path.

    import { Link, useNavigate } from 'react-router-dom';
    
    ...
    
    const navigate = useNavigate();
    
    ...
    
    const handleLoginButton = (e) => {
      e.preventDefault();
    
      const password = loggedUser.map(({ password }) => password)[0];
      console.log(password);
    
      if (!loggedUser) {
        console.log('Error in your email');
      } else {
        console.log('Correct email');
    
        if (passwordInput === password) {
          console.log('and correct password!');
          navigate("/home", { replace: true });
        } else {
          console.log('but wrong password');
          navigate("/", { replace: true });
        }
      }
    };
    
    ...
    
    <Link to={linkTo} onClick={handleLoginButton}>
      <LoginButton>Log In</LoginButton>
    </Link>
    
  3. Note that in the above two suggested solutions that the link target is effectively irrelevant, after clicking the button it may be a completely different target path. The typical solution then is to remove the Link component and use only the button, and use CSS to style it to "look like a link wrapping a button".

    import { useNavigate } from 'react-router-dom';
    
    ...
    
    const navigate = useNavigate();
    
    ...
    
    const handleLoginButton = () => {
      const password = loggedUser.map(({ password }) => password)[0];
      console.log(password);
    
      if (!loggedUser) {
        console.log('Error in your email');
      } else {
        console.log('Correct email');
    
        if (passwordInput === password) {
          console.log('and correct password!');
          navigate("/home", { replace: true });
        } else {
          console.log('but wrong password');
          navigate("/", { replace: true });
        }
      }
    };
    
    ...
    
    <LoginButton
      className="LinkButton" // <-- CSS classname to look like Link/Button UI
      onClick={handleLoginButton}
    >
      Log In
    </LoginButton>