JiSoo's Devlog

[Udemy React 완벽 가이드] Section 15 본문

Frontend/React

[Udemy React 완벽 가이드] Section 15

지숭숭숭 2024. 1. 4. 11:49

양식

  • 개발자 시각에서 폼은 폼과 그 입력 때문에 넓고 다양한 상태를 나타낼 수 있어 굉장히 복잡할 수 있다
  • 하나 이상의 입력 값이 모두 유효하지 않을 수도 있으며 모두 유효할 수도 있고 심지어는 서버로 리퀘스트를 보낸 뒤에 특정 값이 사용 가능한지 확인해야 하는 비동기 유효성 검사를 이용해야 해서 상태를 알 수 없을 수도 있다
  • input 요소에서 포커스를 잃었을 때 입력값의 유효성을 검증하는 경우에 좋은 점은 전체 폼이 제출되고 경고 메시지를 보내기 전에 사용자가 유효한 값을 입력할 수 있다는 것이다

 

 

양식 제출 처리 및 사용자 입력 값 가져오기

  • 사용자 입력을 가져오는 방법에는 크게 두 가지가 있는데 모든 키 입력마다 확인하며 이 값들을 어떤 상태 변수에 저장할 수 있고 ref를 이용해 사용자가 값을 모두 입력했을 때 입력 값을 가져올 수도 있다
  • 폼 안에 있는 버튼을 통해 폼이 제출되면 웹사이트를 제공하는 서버로 HTTP 요청을 보내게 되는데 이 과정은 자동적으로 일어나며 브라우저가 자동적으로 웹사이트를 제공하는 서버로 HTTP 요청을 보낸다
  • ref는 항상 current 프로퍼티를 갖는 객체이므로 리액트에서 ref는 항상 current 프로퍼티를 갖게 된다
  • 즉각적인 유효성 검증을 위해 키 입력마다 입력 값이 필요하다면 ref로는 이 작업이 불가하므로 상태를 사용하는 것이 좋다

 

 

기본 검증 추가하기

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

 

 

"was touched" State 처리하기

const SimpleInput = (props) => {
  const nameInputRef = useRef();
  const [enteredName, setEnteredName] = useState("");
  const [enteredNameIsValid, setEnteredNameIsValid] = useState(false);
  const [enteredNameTouched, setEnteredNameTouched] = useState(false);

  useEffect(()=>{
    if(enteredNameIsValid){
      console.log('Name Input is valid!');
    }
  },[enteredNameIsValid])

  const nameInputChangeHandler = (event) => {
    setEnteredName(event.target.value);
  };
  const formSubmissinHandler = (event) => {
    event.preventDefault();

    setEnteredNameTouched(true);


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

    console.log(enteredName);

    const enteredValue = nameInputRef.current.value;
    console.log(enteredValue);

    //nameInputRef.current.value = ""; =>NOT IDEAL, DON'T MANIPULATE THE DOM
    setEnteredName("");
  };

  const nameInputIsInValid = !enteredNameIsValid && enteredNameTouched;

  const nameInputClasses = enteredNameIsValid
    ? "form-control invalid"
    : "form-control";

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

 

 

초점을 잃은 리액트

  • onBlur핸들러는 자바스크립트 이벤트로 input 요소가 포커스를 따라서 여기에 새로운 함수를 추가하고 event를 써서 바인딩한다
const nameInputBlurHandler = event=>{
    setEnteredNameTouched(true);
    
    if (enteredName.trim() == "") {
      setEnteredNameIsValid(false);
      return;
    }
  }
 
<input
          ref={nameInputRef}
          type="text"
          id="name"
          onChange={nameInputChangeHandler}
          onBlur={nameInputBlurHandler}
          value={enteredName}
        />
 

 

 

리팩토링 및 State 파생

import {useState } from "react";

const SimpleInput = (props) => {

  const [enteredName, setEnteredName] = useState("");
  const [enteredNameTouched, setEnteredNameTouched] = useState(false);

  const enteredNameIsValid = enteredName.trim() !=='';
  const nameInputIsInValid = !enteredNameIsValid && enteredNameTouched;
  
  const nameInputChangeHandler = (event) => {
    setEnteredName(event.target.value);
  };

  const nameInputBlurHandler = event=>{
    setEnteredNameTouched(true);
  }

  const formSubmissinHandler = (event) => {
    event.preventDefault();

    setEnteredNameTouched(true);


    if (!enteredNameIsValid) {
      return;
    }

    setEnteredNameIsValid(true);

    console.log(enteredName);

  

    //nameInputRef.current.value = ""; =>NOT IDEAL, DON'T MANIPULATE THE DOM
    setEnteredName("");
    setEnteredNameTouched(false);
  };


  const nameInputClasses = enteredNameIsValid
    ? "form-control invalid"
    : "form-control";

  return (
    <form onSubmit={formSubmissinHandler}>
      <div className={nameInputClasses}>
        <label htmlFor="name">Your Name</label>
        <input
          type="text"
          id="name"
          onChange={nameInputChangeHandler}
          onBlur={nameInputBlurHandler}
          value={enteredName}
        />
        {nameInputIsInValid && (
          <p className="error-text">Name must not be empty.</p>
        )}
      </div>
      <div className="form-actions">
        <button>Submit</button>
      </div>
    </form>
  );
};

export default SimpleInput;
 

 

 

전체 양식 유효성 관리하기

  • 전체 양식이 유효하기 위해서는 모든 입력이 유효해야 한다
 let formIsValid =false;
  
  if(enteredNameIsValid){
    formIsValid=true;
  }

~~~~

<button disabled={!formIsValid}>Submit</button>
 

 

사용자 지정 입력 훅 추가하기

  • 커스텀 훅을 이용해 상태에 관련된 모든 로직을 다룰 수 있다

SimpleInput.js

import { useState } from "react";

import useInput from "../hooks/use-input";

const SimpleInput = (props) => {
  const {
    value: enteredName,
    isValid: enteredNameIsValid,
    hasError: nameInputHasError,
    valueChangeHandler: nameChangedHandler,
    inputBlurHandler: nameBlurHandler,
    reset: resetNameInput,
  } = useInput((value) => value.trim() !== "");

  const [enteredEmail, setEnteredEmail] = useState("");
  const [enteredEmailTouched, setEnteredEmailTouched] = useState(false);

  const enteredEmailIsValid = enteredEmail.includes("@");
  const enteredEmailIsInvalid = !enteredEmailIsValid && enteredEmailTouched;

  let formIsValid = false;

  if (enteredNameIsValid && enteredEmailIsValid) {
    formIsValid = true;
  }

  const emailInputChangeHandler = (event) => {
    setEnteredEmail(event.target.value);
  };

  const emailInputBlurHandler = (event) => {
    setEnteredEmailTouched(true);
  };

  const formSubmissionHandler = (event) => {
    event.preventDefault();

    if (!enteredNameIsValid) {
      return;
    }

    console.log(enteredName);

    // nameInputRef.current.value = ''; => NOT IDEAL, DON'T MANIPULATE THE DOM
    resetNameInput();

    setEnteredEmail("");
    setEnteredEmailTouched(false);
  };

  const nameInputClasses = nameInputHasError
    ? "form-control invalid"
    : "form-control";

  const emailInputClasses = enteredEmailIsInvalid
    ? "form-control invalid"
    : "form-control";

  return (
    <form onSubmit={formSubmissionHandler}>
      <div className={nameInputClasses}>
        <label htmlFor="name">Your Name</label>
        <input
          type="text"
          id="name"
          onChange={nameChangedHandler}
          onBlur={nameBlurHandler}
          value={enteredName}
        />
        {nameInputHasError && (
          <p className="error-text">Name must not be empty.</p>
        )}
      </div>
      <div className={emailInputClasses}>
        <label htmlFor="email">Your E-Mail</label>
        <input
          type="email"
          id="email"
          onChange={emailInputChangeHandler}
          onBlur={emailInputBlurHandler}
          value={enteredEmail}
        />
        {enteredEmailIsInvalid && (
          <p className="error-text">Please enter a valid email.</p>
        )}
      </div>
      <div className="form-actions">
        <button disabled={!formIsValid}>Submit</button>
      </div>
    </form>
  );
};

export default SimpleInput;
 

use-input.js

import { useState } from "react";

const useInput = (validateValue) => {
  const [enteredValue, setEnteredValue] = useState("");
  const [isTouched, setIsTouched] = useState(false);

  const valueIsValid = validateValue(enteredValue);
  const hasError = !valueValid && isTouched;

  const valueChangeHandler = (event) => {
    setEnteredValue(event.target.value);
  };
  const inputBlurHandler = (event) => {
    setIsTouched(true);
  };

  const reset = () => {
    setEnteredValue("");
    setIsTouched(false);
  };

  return {
    value: enteredValue,
    isValid: valueIsValid,
    hasError,
    valueChangeHandler,
    inputBlurHandler,
    reset
  };
};

export default useInput;
 

 

 

useReducer() 사용하기

import { useReducer } from "react";

const initialInputState = {
  value: "",
  isTouched: false,
};

const inputStateReducer = (state, action) => {
  if (action.type === "INPUT") {
    return { value: action.value, isTouched: state.isTouched };
  }
  if (action.type === "BLUR") {
    return { isTouched: true, value: state.value };
  }
  if (action.type === "RESET") {
    return { isTouched: false, value: "" };
  }
  return inputStateReducer;
};

const useInput = (validateValue) => {
  const [inputState, dispatch] = useReducer(
    inputStateReducer,
    initialInputState
  );

  const valueIsValid = validateValue(inputState.value);
  const hasError = !valueIsValid && inputState.isTouched;

  const valueChangeHandler = (event) => {
    dispatch({ type: "INPUT", value: event.target.value });
  };

  const inputBlurHandler = (event) => {
    dispatch({ type: "BLUR" });
  };

  const reset = () => {
    dispatch({ type: "RESET" });
  };

  return {
    value: inputState.value,
    isValid: valueIsValid,
    hasError,
    valueChangeHandler,
    inputBlurHandler,
    reset,
  };
};

export default useInput;
 

 

728x90