Validating User Input

Add validation to a form component.


Environment

Use the environment you are most comfortable with. I recommend using create-react-app to create a local version of the project so that you can inspect the DOM easily from your browser when debugging. Alternatively, you can work from a blank React template in CodeSandbox. Starter code is provided after the problem specification.

Specification

Write a functional component that imitates a log-in process. The component should be a form consisting of two input fields — for username and password — and a login button beneath them.

The final product
The final product

When the user selects Submit or hits enter, the form should check if the username and password entered match some user stored in the component’s state. If so, an alert should appear including the user’s username (as shown below), and the component should reset to its starting state.

Alert displayed after successful login
Alert displayed after successful login

If the username and password entered do not match an existing count, then (so long as each field is more than six characters long) create a new account, store it for future log-ins, and display an alert to notify the user. Just as the form does on successful login, it should clear the input fields following the alert.

Creating a new account from user input
Creating a new account from user input

If the username and/or password entered is less than six characters, do not create a new account, and display an alert explaining the issue. Do not clear the input fields so that the user may append to what they have already entered:

Alert displayed when the username and/or password is too short
Alert displayed when the username and/or password is too short

Finally, the user should not be allowed to input more than 20 characters in either field. If the user attempts to add a 21st character to their input, do not reflect their change, and display the error in an alert.

Starter Code

Here is some code to get you started:

import React, { useState } from "react";

function App() {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        paddingTop: 20,
      }}
    >
      <ValidatedForm />
    </div>
  );
}

const ValidatedForm = () => {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [accounts, setAccounts] = useState([
    { username: "JohnDoe1", password: "1234567" },
  ]);

  const onSubmit = (e) => {
    // YOUR CODE HERE
  };

  return (
    <form
      style={{
        display: "flex",
        flexDirection: "column",
        border: "solid",
        padding: 10,
      }}
      onSubmit={onSubmit}
    >
      <h3>Login</h3>
      <input
        value={username}
        type="text"
        onChange={/* MORE OF YOUR CODE HERE */ () => {}}
        style={{ marginBottom: 5 }}
      />
      <input
        value={password}
        type="text"
        onChange={/* MORE OF YOUR CODE HERE */ () => {}}
        style={{ marginBottom: 10 }}
      />
      <button style={{ alignSelf: "center" }} onClick={onSubmit}>
        Submit
      </button>
    </form>
  );
};

export default App;

The array in the component’s state that stores existing user objects is prepopulated with one user.

Solution

import React, { useState } from "react";

function App() {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        paddingTop: 20,
      }}
    >
      <ValidatedForm />
    </div>
  );
}

const ValidatedForm = () => {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [accounts, setAccounts] = useState([
    { username: "JohnDoe1", password: "1234567" },
  ]);

  const userExists = (user, pass) => {
    for (const account of accounts) {
      if (account.username == user && account.password == pass) {
        return true;
      }
    }
    return false;
  };

  const onSubmit = (e) => {
    e.preventDefault(); // prevents page refresh
    if (userExists(username, password)) {
      alert("Logged in successfully! Hi, " + username + ".");
    } else if (username.length > 6 && password.length > 6) {
      setAccounts([...accounts, { username, password }]);
      alert("Logged in successfully! Welcome, " + username + ".");
    } else {
      alert("Username and password must be more than 6 characters.");
      return;
    }
    setUsername("");
    setPassword("");
  };

  return (
    <form
      style={{
        display: "flex",
        flexDirection: "column",
        border: "solid",
        padding: 10,
      }}
      onSubmit={onSubmit}
    >
      <h3>Login</h3>
      <input
        value={username}
        type="text"
        onChange={(e) =>
          e.target.value.length < 20
            ? setUsername(e.target.value)
            : alert("Username cannot exceed 20 characters.")
        }
        style={{ marginBottom: 5 }}
      />
      <input
        value={password}
        type="text"
        onChange={(e) =>
          e.target.value.length < 20
            ? setPassword(e.target.value)
            : alert("Password cannot exceed 20 characters.")
        }
        style={{ marginBottom: 10 }}
      />
      <button style={{ alignSelf: "center" }} onClick={onSubmit}>
        Submit
      </button>
    </form>
  );
};

export default App;

First, to tackle the validation that needs to occur while the user completes the form — namely, the input fields cannot hold more than 20 characters — I use a ternary operator in the onChange callback of each input element; the input element only updates its own value (held in state) if the new input is less than 20 characters long. The remaining parts of the solution to this challenge are handled in the onSubmit callback (and the userExists helper function called by onSubmit). We stop the default page refresh and walk through a couple of conditional statements to determine the behavior of the form based on the username and password entered.


useState