textarea 초기 높이값 설정하기 | React-hook-form

Bori·2023년 7월 2일
4

어쨌든 공부

목록 보기
20/41

textarea에 불러온 데이터 내용을 넣어주면..

내용에 맞춰서 textarea의 높이값이 적용되어 나타날까요?
⇒ 아니오.

먼저 게시글을 작성했습니다.
그리고 게시글 수정하기 페이지로 이동합니다.

작성한 게시글게시글 수정 페이지

쨔잔~ 작성된 게시글의 모든 내용이 보이지 않고 첫 줄만 보입니다!

textarea에 코드를 살펴봅시다.

<textarea
  id="content"
  rows={1}
  {...register('content', {
    required: true,
    onChange: textareaAutosize,
  })}
/>

// textareaAutosize 코드
const textareaAutosize: FormEventHandler<HTMLTextAreaElement> = (e) => {
  const element = e.target as HTMLTextAreaElement;
  element.style.height = 'auto';
  element.style.height = `${element.scrollHeight}px`;
};

textarearows={1}을 적용해놓은 상태이기 때문에 첫 줄만 보입니다.
rows={1}를 삭제해도 rows의 기본값이 2이기 때문에 rows를 삭제하면 2줄까지만 보입니다.
그리고 onChange 이벤트가 발생하면 textareaAutosize로 인해 textarea의 높이값이 자동 조절되면서 모든 내용이 나타납니다.

onChange 이벤트 발생 전onChange 이벤트 발생 후

뭐.. 내용은 문제없이 나타나지만 초기에 모든 내용이 보이지 않는다는 것은 문제가 있습니다.

그래서 게시글 수정 페이지 진입 시 작성했던 내용이 온전히 보이게 하기 위해 onFocus 이벤트를 사용해보겠습니다.
textarea에 포커스 될 때 onChange 이벤트에 적용했던 것처럼 textareascrollHeight 값을 적용하는거죠!

페이지 진입 시 textarea에 포커스 주기

React-hook-form은 setFocus 메소드를 제공합니다.

setFocus

setFocus: (name: string, options: SetFocusOptions) => void

Props

  • name : string, 포커스 할 input field 이름
  • options : { shouldSelect : boolean }, 포커스에 입력 내용을 선택할지 여부
    • { shouldSelect : true }로 설정하면 해당 내용이 선택되어 있습니다.
      (예: 텍스트의 경우 드래그 된 상태)

예시 코드

import { useForm } from "react-hook-form"

type FormValues = {
  firstName: string
}

export default function App() {
  const { register, handleSubmit, setFocus } = useForm<FormValues>();
  const onSubmit = (data: FormValues) => console.log(data);

  useEffect(() => {
    setFocus("firstName");
  }, [setFocus]);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName")} placeholder="First Name" />
      <input type="submit" />
    </form>
  )
}

useEffect 안에 setFocus 메소드를 적용하고 포커스를 줄 입력 필드 이름을 인자로 넘겨주면 끝입니다.

onFocus 이벤트

그럼 포커스 이벤트가 발생했을 때의 코드를 작성해봅시다!
textareaAutosize에 적용했던 것과 동일하게 해당 요소의 높이값에 scrollHeight를 적용합니다.

const handleOnFocusTextarea: FocusEventHandler<HTMLTextAreaElement> = (e) => {
  const { target } = e;
  target.style.height = `${target.scrollHeight}px`;
};

혹시 위의 코드가 동작하지 않는다면 setTimeout을 이용하여 비동기적으로 처리해봅니다.

// setTimeout 적용하여 비동기적으로 처리하기
const handleOnFocusTextarea: FocusEventHandler<HTMLTextAreaElement> = (e) => {
  const { target } = e;
  setTimeout(() => {
    target.style.height = `${target.scrollHeight}px`;
  }, 0);
};

React-hook-form의 register는 onFocus 메소드를 제공하지 않습니다. 따라서 해당 요소에 직접 onFocus 이벤트에 위의 코드를 적용합니다.

<textarea
  id="content"
  rows={1}
  {...register('content', {
    required: true,
    onChange: textareaAutosize,
  })}
  onFocus={handleOnFocusTextarea} // register 내부에 작성하지 않습니다.
/>

최종 코드

const EditPage = () => {
  const { register, setFocus } = useForm<{ content: string; }>();
  
  useEffect(() => {
    setFocus('content');
  }, [setFocus]);
  
  const handleOnFocusTextarea: FocusEventHandler<HTMLTextAreaElement> = (e) => {
    const { target } = e;
    setTimeout(() => {
      target.style.height = `${target.scrollHeight}px`;
    }, 0);
  };
  
  return (
    ...
    <textarea
      id="content"
      rows={1}
      {...register('content', {
        required: true,
        onChange: textareaAutosize,
      })}
      onFocus={handleOnFocusTextarea}
    />
  )
}

최종 화면

게시글 수정하기 페이지 진입하면 다음과 같이 게시글의 모든 내용이 나타나고, 커서가 게시글의 마지막에서 깜빡이는 모습을 볼 수 있습니다!

마무리

이렇게 적용하기 전에 다시 한 번 contentEditable을 적용해보았습니다. contentEditable을 사용하면 높이 조절에 대해 신경쓰지 않아도 되기 때문인데요..
근데 처참히 실패했습니다.
이전 글의 문제점에서의 개행이 문제입니다. 이놈의 개행 문자..!!
제출 버튼 클릭 시 연속된 개행 문자를 replace를 이용해서 하나의 개행문자만 적용되도록 해보았는데.. 만약 수정 페이지에서 내용 변경 안하고 수정 버튼을 누른다?? 그럼 애먼 연속된 개행문자만 계속 사라지는 현상이 발생합니다.
그래서 contentEditable을 사용하는 것은 마무리 하고 다른 방법을 찾다가 생각보다 쉽게 해결이 되었습니다. 휴-

참고

0개의 댓글