본문 바로가기
  • 삽질하는 자의 블로그
React/React-Basic

10. 현실적인 커스텀 훅 만들기, WITH. validation, onBlur 이벤트리스너

by 이게뭐당가 2022. 12. 14.

골머리가 아프다

 

함수, 상태 를 반환하는 커스텀 훅 을 이용하여  Validation 이 있는 수백개의 INPUT 이 있는 컴포넌트를 리팩토링 해본다.

 

해봤는데 더 복잡하다. 이렇게 사용할 것은 아니지만... 연습으로 한번 보도록 하자.

 

1. 간단한 Input 컴포넌트

 

: input이 있고, validation 을 거쳐, 나온 State 로 error가 나오면 error를 표시하는 컴포넌트

 

    import styles from "./basic-form.module.css";
    import AlertFun from "./alert";
    import { useEffect, useState } from "react";

    function BasicForm() {
        const [inputText, setInputText] = useState("");
        const [inputEmail, setInputEmail] = useState("");       // 만약 input 이 수백개라면? 수백개를 다 쓰나?

        const [textIsTouched, setTextIsTouched] = useState(false);
        const [emailIsTouched, setEmailIsTouched] = useState(false);

        const [error, setError] = useState(false);

        function validationCheck() {
            if (textIsTouched && emailIsTouched) {
                if (
                    inputText.trim() === "" ||
                    inputEmail.trim() === "" ||
                    !inputEmail.includes("@")
                ) {
                    setError(true);
                }
            }
        }

        useEffect(() => {
            validationCheck();
        }, [inputText, inputEmail]);

        function inputNameHandler(e) {                      // 만약 input 이 수백개라면? 수백개를 다 쓰나?
            setInputText(e.target.value);
        }

        function inputEmailHandler(e) {
            setInputEmail(e.target.value);
        }
        return (
            <form className={styles.maindiv}>
                <label htmlFor="text"> 이름</label>
                    ...
            </form>
        );
    }

    export default BasicForm;

 

 

2. 재사용 가능한 커스텀 훅의 생성

   1) 우선 부모측에서 리팩토링의 결과로 사용할 것들을 생각한다.

 

   2) 여러곳에 사용하기 위한 커스텀 훅이므로, 유동적으로 만들고 싶을때, 무엇을 넣어주어야 하는지 생각한다.

    [리팩토링 해야 하는 것들]
        1. useState
        2. validation
        3. validation 하기 위한, inputHandelr 함수  => 아웃소싱해서 호출할것
        4. validation 하기 위한, isTouchedHandler 함수  => 아웃소싱해서 호출할것

    [return 해야 하는 상태 (부모측에서 사용할 값)]
        1. 입력되는 값의 상태
        2. 인풋의 validation 결과
        3. 이벤트 리스너에 함수로 들어갈 함수들 (inputHandelr, isTouchedHandler )  => 아웃소싱해서 호출하므로

    [부모로부터 받아야 하는 값들]
        1. validation 로직

만들어보자

 

<커스텀 훅> 

      import { useState } from "react";

        function useInput(inValidationCheck) {
            const [inputValue, setInputValue] = useState(); // 입력되는 값의 상태
            const [inputIsTouched, setInputIsTouched] = useState(false); // touched 되어있는지의 상태

            const inputIsInvalid = inValidationCheck(inputValue); // inputValue 를 체크해보니, invalid 한 상태냐? ***
            const hasError = inputIsTouched && inputIsInvalid; // 터치 햇는데, invalid 한 상태냐? => 에러발생

            function inputHandler(e) {          // 입력값 받기위한 함수       ==>  이 커스텀훅을 사용하는곳에서, 호출가능하다.
                setInputValue(e.target.value);
            }

            function inputTouchHandler() {      // 터치되었는지 확인하는 함수     ==>  이 커스텀훅을 사용하는곳에서, 호출가능하다.
                setInputIsTouched(true);
            }

            return {
                inputValue,
                inputIsTouched,
                hasError,
                inputHandler,
                inputTouchHandler,
            };
        }
        export default useInput;

 

 

3. 재사용 가능한 커스텀 훅의 사용

   import styles from "./basic-form-refac.module.css";
    import AlertFun from "./alert";
    import { useEffect, useState } from "react";
    import useInput from "../hooks/use-input";

    function BasicFormRefac() {

        const [error, setError] = useState(false);

        const inputTextResult = useInput((inputText) => String(inputText).trim() === ""); // 커스텀훅의 사용, validation 인수 입력
        const inputEmailResult = useInput((inputEmail) => !String(inputEmail).includes("@")); // not a function error 핸들

        const {
            inputValue: inputText,	// 중복되지 않도록, 가지고 온 변수, 함수 이름을 변경
            hasError: inputTextError,
            inputIsTouched: inputTextIsTouched,
            inputHandler: inputTextHandler,
            inputTouchHandler: inputTextTouchHandler,
        } = inputTextResult;

        const {
            inputValue: inputEmail,
            hasError: inputEmailError,
            inputIsTouched: inputEmailIsTouched,
            inputHandler: inputEmailHandler,
            inputTouchHandler: inputEmailTouchHandler,
        } = inputEmailResult;

        function validationCheck() {
            if (inputTextIsTouched && inputEmailIsTouched) {
                if (inputTextError || inputEmailError) {
                    setError(true);
                }
            }
        }

        useEffect(() => {
            validationCheck();
        }, [inputText, inputEmail, inputTextIsTouched, inputEmailIsTouched]);

        return (
            <form className={styles.maindiv}>
                <label htmlFor="text"> 이름</label>
                <input
                    type={"text"}
                    id={"text"}
                    onChange={inputTextHandler}
                    onBlur={inputTextTouchHandler}
                />
                <label htmlFor="email"> 이메일</label>
                <input
                    type={"email"}
                    id={"email"}
                    onChange={inputEmailHandler}
                    onBlur={inputEmailTouchHandler}
                />
                {error && <AlertFun />}
            </form>
        );
    }

    export default BasicFormRefac;

 

 

4. 주요 부분 해석 *****

  1) 부모가 커스텀훅에게 함수를 넣어주고, 커스텀 훅 안 있는 인수를 넣어, 커스텀 훅 안에서 함수를 사용

       , 부모에게 리턴 *****

 

<커스텀 훅>

    import { useState } from "react";

    function useInput(inValidationCheck) {
        const [inputValue, setInputValue] = useState(); // 입력되는 값의 상태
        const [inputIsTouched, setInputIsTouched] = useState(false); // touched 되어있는지의 상태

        const inputIsInvalid = inValidationCheck(inputValue); // inputValue 를 체크해보니, invalid 한 상태냐? ***
        
<부모>
 	const inputTextResult = useInput((inputText) => String(inputText).trim() === ""); // 커스텀훅의 사용, validation 인수 입력

 커스텀 훅에서 자신의 inputValue State 를 넣어 사용하는 함수를 만들었다. 함수는 아직 지정되지 않았다.

부모에서 validation 로직을 넣어주었다.

(inputText) => String(inputText).trim() === ""

 

자식의 inValidationCheck 함수의 인수(argument)에는 자신의 inputValue 를 넣는다 [고정됨]

부모에서 넣어준 함수는, 자식이 그대로 가져가, 부모가 "자 여기 파라미터에 인수를 넣거라"

    하여, 자식은 해당 함수에 자신이 고정한 인수 값을 넣는다.

 

<커스텀훅>
const inputIsInvalid = inValidationCheck(inputValue) 	// 커스텀 훅에서 실행된 함수는

<부모>
(inputText) => String(inputText).trim() === "" 		// 부모에서 준 함수와 만나

<커스텀훅>
const inputIsInvalid = (inputValue) => String(inputValue).trim() === ""		// 이 되어 커스텀 훅에서 사용되고

inputIsInvalid 으로 결과값이 들어가고

부모에서 사용 가능하도록 return 한다.

 

  2) 커스텀 훅에서 만든 함수를, 부모가 받아서 사용.

     (이때, 부모로부터 받은 것이 없으므로) 부모는 그대로, 함수 자체를 받아서 사용

<커스텀 훅>

    function useInput(inValidationCheck) {
			...
        function inputHandler(e) {          // 입력값 받기위한 함수       ==>  이 커스텀훅을 사용하는곳에서, 호출가능하다.
            setInputValue(e.target.value);
        }
			...


<부모>
    const inputTextResult = useInput((inputText) => String(inputText).trim() === ""); // 커스텀훅의 사용, validation 인수 입력

    const {
        inputValue: inputText,	// 중복되지 않도록, 가지고 온 변수, 함수 이름을 변경
        hasError: inputTextError,
        inputIsTouched: inputTextIsTouched,
        inputHandler: inputTextHandler,
        inputTouchHandler: inputTextTouchHandler,
    } = inputTextResult;

return(
    		...
        <input type={"text"} id={"text"} onChange={inputTextHandler} />  //받아서 사용
    )

커스텀 훅은 inputHanlder(e) 함수를 그대로 부모에게 넘겨준다. (부모로 부터 받은 것이 없으므로)

커스텀 훅을 계속 생각하면, 뭔가 이상해보이지만, 사실 그냥 코드 리팩토링이다.

함수 가 아닌, 그저 코드 라고 생각하고 넘기는 것이다.

 

오히려 이게 기존에 쓰던 방식 그대로이므로, 헷갈릴 것이 없어야 한다. 근데 나는 헷갈렸다.

 

부모는  함수코드를 그대로 가져와 그냥 쓴거다. 별거 없다.

댓글