코드노트

REACT Hooks 노트 custom hook (form control) 본문

Code note/리액트

REACT Hooks 노트 custom hook (form control)

코드노트 2023. 3. 13. 01:27

 

자바스크립트에서도 반복되는 로직들을 함수로 만들어서 사용했듯이

리액트에서도 컴포넌트 단위로 만들다보면 반복되는 로직이 발생한다.

input Validation 같은 예를 볼 수 있을 것 같다.

 

이번에 진행중인 프로젝트중에서 form control 중에서 컴포넌트 안에 사용되는 함수가 많다보니

코드가 너무 길어졌다. 관련하여 custom hook를 적용해보는걸 기록해보려고 한다.

 

리액트 공식문서에도 form관련 라이브러리를 보여주지만

사용하는 값들을 반환해주는것들이 비슷하다보니 이해하고 넘어가면 도움이 될것 같다.

 


사용하는 방법은 간단하다.

function use만들이름 () {
	// ...
}

- use를 앞에 붙여주고 함수이름을 만들면 리액트훅처럼 사용할 수 있다.


Custom Hooks 사용전 코드

- 현재 코드에서 handleSubmit 함수를 Custom Hooks로 만들어보려고 한다.

export default function CreateIssue() {
  const inputRef = useRef();
  const textareaRef = useRef();
  const [inputValues, setInputValues] = useState({ title: "", body: "" });
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  function handleSubmit(event) {
    event.preventDefault();

    setIsSubmitting(true);
    const validateResult = validate(inputValues);
    setErrors(validateResult);

    const refs = { title: inputRef, body: textareaRef };
    const errorKeys = Object.keys(validateResult);
    if (errorKeys.length !== 0) {
      const key = errorKeys[0];
      alert(validateResult[key]);
      refs[key].current.focus();
      // ref control
      setIsSubmitting(false);
      return;
    }

    if (errorKeys.length === 0) {
      console.log("Submit 성공");
    }
  }

  function onChange(event) {
    const { name, value } = event.target;
    setInputValues({ ...inputValues, [name]: value });
  }

  return (
    <div className={styles.container}>
      <div className={styles.avatar}></div>
      <div className={cx(styles.inputWrapper, styles.border)}>
        <form onSubmit={handleSubmit}>
          <TextField
            ref={inputRef}
            name="title"
            placeholder="Title"
            value={inputValues.title}
            onChange={onChange}
            error={errors.title}
          />
          <TextField
            ref={textareaRef}
            type="textarea"
            name="body"
            placeholder="Leave a comment"
            value={inputValues.body}
            onChange={onChange}
            error={errors.body}
          />

          <div className={styles.buttonWrapper}>
            <Button
              type="submit"
              style={{ backgroundColor: "#238636", color: "#fff" }}
              disabled={isSubmitting}
            >
              Submit new issue
            </Button>
          </div>
        </form>
      </div>
    </div>
  );
}

function validate(values) {
  let errors = {};
  // 입력값이 없으면 errors{title: ""}
  if (values.title === "") {
    errors = { title: "타이틀은 필수값입니다." };
  }
  // 입력값이 있으면 빈객체
  return errors;
}
  • 현재 handleSubmit 함수는 input과 textarea에 입력값이 없으면 alert 창을 띄어준다.
  • 입력값, 에러상태, 전송상태를 감지해야하기 떄문에 useState 3개를 사용중이다.

- 현재 handleSubmit 에서 사용되는 함수 그리고 출력하여 적용되는 값들을 넘겨줄게 무엇인지 체크를 먼저하자.

- return 을 통해서 값만 넘겨주게되면? Hooks로서의 역할은 하고 있는거다.

 

import { useState } from "react";

export function useForm(

  return {
    inputValues,
    onChange,
    isSubmitting,
    errors,
    handleSubmit,
  };
}

- 이렇게 기존에 사용되고 있었던 값들을 return 을 통해서 미리 작성해두면 커스텀 훅을 만들때 막히지 않는다.

- 넘겨줄 값들을 하나하나 체크해가며 로직을 작성할 수 있었다.

 


Custom Hooks 로 만들어 낸 Hooks

import { useState } from "react";

export function useForm({
  initaialValues,
  validate,
  refs,
  onSuccess, // 성공
  onErrors, // 애러
  onSubmit, // 값이 전달될 때
}) {
  const [inputValues, setInputValues] = useState(initaialValues);
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  function handleSubmit(event) {
    event.preventDefault();

    setIsSubmitting(true);
    const validateResult = validate(inputValues);
    setErrors(validateResult);

    const errorKeys = Object.keys(validateResult);
    // const refs = { title: inputRef, body: textareaRef };
    if (errorKeys.length !== 0) {
      const key = errorKeys[0];
      alert(validateResult[key]);
      onErrors();
      refs[key].current.focus();
      // ref control
      setIsSubmitting(false);
      return;
    }

    if (errorKeys.length === 0) {
      onSubmit();
      return;
    }
  }

  function onChange(event) {
    const { name, value } = event.target;
    setInputValues({ ...inputValues, [name]: value });
  }

  return {
    inputValues,
    onChange,
    isSubmitting,
    errors,
    handleSubmit,
  };
}

- 처음에 함수만 넘겨주고 use로 감싸주면 끝일줄알았지만 넘겨줄 값들의 함수를 다시 작성하면서 어떻게 값을 넘겨주어야할지 고민했다.

- input에만 적용하는게 아니라 textarea에도 적용할 수 있어야하기 때문에 validate같은 함수는 합치는게 아닌 값을 받아서 적용이 되도록 수정해주었다.

- 다시 만든 코드를 커스텀하면서 모듈로 나누는? 형식으로 수정하다보니 재사용에 있어서도 반영할 수 있어 좋은 방법중에 하나인거 같다.

- form control에 있어서는 라이브러리를 사용하기에 사용하는 방법에 있어서는 비슷한면이 있어 공부아닌 공부가 된거 같다.

 

Formik, React Hook Form에 관해서도 미니프로젝트를 진행하면서 사용방법을 익혀보도록 해야겠다.

 

 

 

Formik

React hooks and components for hassle-free form validation. The world's leading companies use Formik to build forms and surveys in React and React Native.

formik.org

 

 

Home

React hook for form validation without the hassle

react-hook-form.com