React note #12

Yechan Jeon·2021년 12월 28일
0

React dev

목록 보기
15/18
post-thumbnail

Forms & User input in detail


What makes form complex?

  1. Forms and inputs can assume different states(one or more inputs are invalid vs All inputs are valid).

  2. When to validate form

  • After user finish typing
  • When input lose focus
  • whenever user type in input

Dealing with form submission & getting user input

About getting input value, There is two ways.(useState or useRef);
1. useState

  • you can get input value whenever user type.
  • you can bind input state to input element so that you can clear the input
    const [enteredName, setEnteredName] = useState("");
 <input
          type="text"
          id="name"
          onChange={nameInputHandler}
          ref={inputRef}
          value={enteredName}
        />
  1. useRef
  • you can get input value when user finish typing
  • you also can clear the input with ref, but this is not ideal way because it manipulated realDOM value directly and this is vanilla javascript thing.

Adding Basic validation - when form is submitted

Adding condition in your form submit handler.

if (enteredName.trim() === "") {
      return;
    }

we also need some error messages and style of invalidity for better UX
To achieve this, we can use 'useState' with boolean state and send error message or style to user as of state.

const [validName, setValidName] = useState(true);
.
.
const nameInputClasses = validName ? "form-control" : "form-control invalid";
.
.
<div className={nameInputClasses}>
        <label htmlFor="name">Your Name</label>
        <input
          type="text"
          id="name"
          onChange={nameInputHandler}
          ref={inputRef}
          value={enteredName}
        />
        {!validName && <p className="error-text">Name must not be empty</p>}
      </div>

For more specific error condition, we can add more state and useEffect().
It is to send http request to server in real world app that we use useEffect here.
we can add a state about whether this input are touched or not so that we can send some error style to user.

 const [touchedName, setTouchedName] = useState(false);
.
.
const submitHandler = (evt) => {
    evt.preventDefault();

    setTouchedName(true);
	.
    .
  }; // if form is submitted, we guess that user already touched input.

const nameInputIsInvalid = !validName && touchedName; //more specific condition

 return (
    <form onSubmit={submitHandler}>
      <div className={nameInputClasses}>
        <label htmlFor="name">Your Name</label>
        <input
          type="text"
          id="name"
          onChange={nameInputHandler}
          ref={inputRef}
          value={enteredName}
        />
        {nameInputIsInvalid && (
          <p className="error-text">Name must not be empty</p>
        )}
      </div>
      <div className="form-actions">
        <button>Submit</button>
      </div>
    </form>
  );

Form validation when input lost focus

Regarding to losing focus action, we have built-in event 'onBlur'.
Taking same logic from submit handler, create new handler and give it to 'onBlur' event.

const nameInputBlurHandler = (evt) => {
    setTouchedName(true);
    if (enteredName.trim() === "") {
      setValidName(false);
      return;
    }
  };

return (
  .
  .
  <input
          type="text"
          id="name"
          onChange={nameInputHandler}
          ref={inputRef}
          value={enteredName}
          onBlur={nameInputBlurHandler}
        />
   .
   .
   )

By the way, we have duplicated logic in here, so we need to refactor code surely.

Form validation on every key stroke + Refactoring duplicate logic

To detect any changes or invalid input on every key stroke, we need to use similar logic in input changing handler.

const nameInputHandler = (evt) => {
    setEnteredName(evt.target.value);
    if (evt.target.value.trim() !== "") {
      setValidName(true);
    }
  };

The reason that we use event.target.value in if condition is react state changing is scheduled process(asynchronous). So if we just use 'enteredName' state in this condition, we will just get old previous state because the function process is not end yet.

Then Let's take a look at refactoring part.
Since we have changed valid state as to our input state, we don't need valid state actually.
Whenver input state is changed, the whole component function will be re-rendered. Even though we don't have any valid state, if we just set some condition varibale from this state, it will be also re-rendered altogether.

Here is refactored code.

import { useState } from "react";

const SimpleInput = (props) => {
  const [enteredName, setEnteredName] = useState("");
  const [touchedName, setTouchedName] = useState(false);

  const validName = enteredName.trim() !== "";
  const nameInputIsInvalid = !validName && touchedName;

  const nameInputHandler = (evt) => {
    setEnteredName(evt.target.value);
  };

  const nameInputBlurHandler = (evt) => {
    setTouchedName(true);
  };

  const submitHandler = (evt) => {
    evt.preventDefault();

    setTouchedName(true);

    if (!validName) {
      return;
    }

    console.log(enteredName);

    setEnteredName("");
    setTouchedName(false);
  };
  const nameInputClasses = nameInputIsInvalid
    ? "form-control invalid"
    : "form-control";
  return (
    <form onSubmit={submitHandler}>
      <div className={nameInputClasses}>
        <label htmlFor="name">Your Name</label>
        <input
          type="text"
          id="name"
          onChange={nameInputHandler}
          value={enteredName}
          onBlur={nameInputBlurHandler}
        />
        {nameInputIsInvalid && (
          <p className="error-text">Name must not be empty</p>
        )}
      </div>
      <div className="form-actions">
        <button>Submit</button>
      </div>
    </form>
  );
};

export default SimpleInput;

Multiple inputs validation

In real world app, there is a few more inputs in a form.
Regarding to many inputs, we should change overall form validity.

let formIsValid = false;
  if (validName) {
    formIsValid = true;
  }

With pre-defined valid input booleans, we can set overall validity boolean.(we just have one input here though.)

There is also a third-party library which handle form content 'Formik'.

profile
방황했습니다. 다시 웹 개발 하려고요!

0개의 댓글