Safari on iOS/Firefox 를 완벽하게 지원하는 React Hook Form

16418 단어 ReactTypeScripttech
리액트 훅 포머스의 발리가 사파리온 iOS/Firefox에서 의도한 대로 움직이지 않자 대책을 마련한 것이다.
2021/11/26 업데이트
  • Firefox에 대한 보완
  • 코드를 간단하게 리셋
  • 하고 싶은 일: 검증 오류가 발견되면 오류 원인을 표시하고 싶은 input 요소


    유효성 검사 오류가 발견되면 다음을 수행합니다.
  • focus를 오류 요소에 대한 input 요소(화면 밖에서 스크롤 표시)
  • 상기 input 요소 근처에 오류 메시지 보이기
  • React Hook Forms를 사용하면 간단히 구현할 수 있으며 일반적인 구조라고 생각합니다.
  • 정상 가동의 예

  • 관심 있는 사람은 여기서부터 조작하세요.
    https://react-hook-form-sample.vercel.app/sample/iphone-validation-focus/ok

    질문: 크롬은 OK지만 Safari on iOS와 Firefox는 NG입니다.


    Safari on iOS와 라디오/checkbox에서 오류가 발생했을 때 화면 스크롤에는 동작이 없습니다.
    인풋 요소는 화면 밖에서 오류 메시지를 볼 수 없다며 "발송 버튼을 누르면 작동하지 않는다! 이유도 분명하지 않다!"이러면 곤란해.
    재현 환경을 만들었으니 관심 있는 사람은 해보세요.
    https://react-hook-form-sample.vercel.app/sample/iphone-validation-focus/ng
    Firefox 스크롤이 충분하지 않습니다.인풋 요소의 하부가 은근히 보이는 정도입니다.

    2021/11/24 현재 검증된 최신 환경

  • react-hook-form: ^7.20.2
  • Safari(iPhone): Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1
  • 이유: HTML InputElementfocus () 의 동작은 브라우저에 따라 다릅니다


    react-hook-form에서 오류가 발생한 원인은 Input 요소의 focus ()입니다.그러나 사파리(iPhone/iPad)의 라디오/checkbox는 포커스()라고 불러도 화면을 굴리지 않는다.
    참조: https://stackoverflow.com/questions/56137175/ios-safari-chrome-wont-scroll-up-to-show-validation-error-message-on-radio-inpu
    (자세가 너무 길어서 한 화면으로 줄이는 게 좋을 것 같은데...)

    대책: onFocus에서 scrollIntoView () 를 실행한 후 화면 스크롤


    input의 패키지 구성 요소 을 만들었습니다. 대상 요소가focus 이벤트가 발생할 때scrollIntoView () 스크롤 화면을 실행합니다.

    input 패키지 구성 요소 만들기


    Input.tsx
    type InputProps = React.DetailedHTMLProps<
      React.InputHTMLAttributes<HTMLInputElement>,
      HTMLInputElement
    >;
    
    const Input = React.forwardRef<HTMLInputElement, InputProps>(
      (props, ref) => {
        const onFocus: FocusEventHandler<HTMLInputElement> = (event) => {
          // バリデーションエラー発生時にradio/checkboxのinputにしっかりスクロールしない問題のワークアラウンド
          // 【問題詳細】
          // Safari on iOS: 全くスクロールしない, FireFox,IE:スクロールが不十分でエラーメッセージが見えない,
          if (props.type !== "radio" && props.type !== "checkbox") {
            return;
          }
    
          try {
            const element = event.currentTarget;
            // radio/checkboxをタップしたときはスクロールしない
            if (isInviewAll(element)) return;
    
            // バリデーションエラー発生時のみスクロールする
            // 注意:本対応はIE対応できていない(scrollIntoViewはIE非対応)
            element.scrollIntoView({
              block: "center",
              behavior: "auto",
            });
          } catch (e) {
            console.error(e);
          }
        };
    
        return <input ref={ref} onFocus={onFocus} {...props} />;
      }
    );
    
    const isInviewAll = (element: HTMLInputElement) => {
      const rect = element.getBoundingClientRect();
    
      // 1 < rect.top は firefox対応
      return 1 < rect.top && rect.bottom < window.innerHeight;
    };
    
    Input.displayName = "Input";
    
    export default Input;
    
    react-hook-form의 공식 수첩을 참고하여 포장 부품을 제작하였습니다.
    https://react-hook-form.com/ts#UseFormReturn

    사용 예: 제작된 <Input>은 <input>와 같이 사용할 수 있습니다


    sample.tsx
      const {
        register,
        formState: { errors },
        handleSubmit,
      } = useForm();
    
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <div>
            <p>Q1. 好きなプログラミング言語はなんですか?</p>
            {errors?.language && (
              <p className="text-red-500 py-2 px-2">
                <span>選択してください</span>
              </p>
            )}
            <Input
              type="radio"
              value="TypeScript"
              {...register("language", { required: true })}
            />
            <label>TypeScript</label>
            <button
              type="submit"
              className="text-white bg-blue-600 py-2 px-4 rounded-md"
            >
              送信
            </button>
          </div>
        </form>
      );
    

    끝말


    이것은 이번에 만든 소스 코드다.
    https://github.com/daisuke85a/react-hook-form-sample
    더 좋은 방법이 있으니까 댓글과 MR을 받으면 기쁠 거예요!

    좋은 웹페이지 즐겨찾기