0

I have a login form. The user types their username/password into the form and submits it. The form uses fetch() to send a request to the backend API and get a response back.

I'd like to display a message from the API on the page. It'll be {apiErrorMessage}

For example, the user's account could be locked. The Password could be wrong. The email address could not be confirmed yet.

In the old days I would do this using Partial Page updates AJAX using C# Razor pages. This is a NextJS project and have no idea how to do this.

Should I be using Server Side Rendering? Should I be using Client Side Rendering? UseEffect() hook?

I'd still like the page to look good for SEO purposes.

I'm thinking maybe we have to use a second page to do this with SSR? LoginResult page or something?

Any Help Appreciated :) Thanks!

const Login = () => {

    let apiErrorMessage = "";

    const loginFormSubmit = async (event) => {

    event.preventDefault()

    // Get data from the form.
    const data = {
      username: event.target.username.value,
      password: event.target.password.value,
    }

    const JSONdata = JSON.stringify(data)

    const response = await fetch(`/api/login`)

    const result = await response.json()

    if (response.ok){
        router.push('/dashboard');
    }
    else {
        // Get the error message from API
        apiErrorMessage = result.message;
    }
}


return (
        <div>
            <form onSubmit={loginFormSubmit}>
                <div className="mb-4">
                    <label htmlFor="username"><b>Username</b></label>
                    <input type="text" id="username" required/>
                </div>
                <div className="mb-4">
                    <label htmlFor="password"><b>Password</b></label>
                    <input type="password" id="password" required/>
                </div>
                <button type="submit">Login</button>
            </form>
        </div>
        <div>
            <p>{apiErrorMessage}</p>
        </div>
 )
}

export default Login;
Scottish Smile
  • 455
  • 7
  • 22

2 Answers2

1

you don't need to create a page for that you can create a simple useState hook :

const [errorMsg,setErrorMsg] = useState('');

when the user fails to login :

else {
   // Get the error message from API
   setErrorMsg(result.message)
}

Now under your <form> you create a <div> that will be only shown when errorMsg !== '' :

{errorMsg === '' ? '' : (<div>
   <p>login failed : ${errorMsg}</p>
</div>)}

as simple as that.

Ahmed Sbai
  • 10,695
  • 9
  • 19
  • 38
1

I can't think of a reason you'd need good SEO for a login component that just accepts user input and returns an error response or forwards them to a page.

Since everything inside /pages is a Client component by default, I think your best bet is simply using state and effects. Here's an example with some slight modifications to your code:

import {useState} from 'react';
import { useRouter } from 'next/navigation';

const Login = () => {
  const [apiErrorMessage, setApiErrorMessage] = useState('');
  const router = useRouter();

  const loginFormSubmit = async (event: any) => {
    event.preventDefault();

    // Get data from the form.
    const data = {
      username: event.target.username.value,
      password: event.target.password.value,
    };

    const JSONdata = JSON.stringify(data);

    const response = await fetch(`/api/login`);

    const result = await response.json();

    if (response.ok) {
      router.push("/dashboard");
    } else {
      // Get the error message from API
      setApiErrorMessage(result.message);
    }
  };

  return (
    <div>
      <div>
        <form onSubmit={loginFormSubmit}>
          <div className="mb-4">
            <label htmlFor="username">
              <b>Username</b>
            </label>
            <input type="text" id="username" required />
          </div>
          <div className="mb-4">
            <label htmlFor="password">
              <b>Password</b>
            </label>
            <input type="password" id="password" required />
          </div>
          <button type="submit">Login</button>
        </form>
      </div>
      <div>
        <p>{apiErrorMessage}</p>
      </div>
    </div>
  );
};

export default Login;
Wesley LeMahieu
  • 2,296
  • 1
  • 12
  • 8
  • 1
    Yeah React's local useState() or a global state provider like Redux or Zustand is the way to go here. I guess all the technical nextjs stuff just threw me for a loop. I didn't think we could do it this way. – Scottish Smile Feb 11 '23 at 14:15