[React] forwardRef() ref 전달하기

summereuna🐥·2023년 5월 1일
0

React JS

목록 보기
48/69

아이디와 패스워드를 입력한 후 로그인하는 폼이 있다고 하자.
만약 사용자가 아이디와 패스워드를 입력하지 않은 채 로그인 버튼을 클릭할 경우, 자동으로 인풋에 포커스를 맞추게 하고 싶은 경우 forwardRef()를 사용하여 이를 구현할 수 있다.

forwardRef()는 React 컴포넌트에 ref 프롭을 넘겨 내부의 HTML 엘리먼트에 접근할 수 있게 한다.

📍 Input 컴포넌트

import React, { forwardRef, useImperativeHandle, useRef } from "react";

import classes from "./Input.module.css";

const Input = forwardRef((props, ref) => {
  //일반 인풋에 ref 훅 사용해서 focus 메소드 호출하면 된다.
  const inputRef = useRef();
  //포커스 하려면 useEffect를 사용하여 컴포넌트가 렌더링된 후에 코드를 실행할 수 있다.

  //   useEffect(() => {
  //     //컴포넌트 렌더링 주기 후 마다 실행되는 이펙트 코드
  //     //focus() 메소드는 인풋 DOM 객체에 사용할 수 있는 메소드이기 때문에 이렇게 접근 가능
  //     inputRef.current.focus();

  //     /*
  //     한 번만 실행되게 하려면 빈 배열을 의존성 배열로 넣으면 됨
  //     그런데 이렇게 하면 렌더링 되는 최신 인풋은 두 번째 인풋인 비번 인풋이므로 로드하면 이메일 인풋은 빨갛게 시작하고 포커스가 패스워드에 맞춰져 버림

  //     리액트, 바닐라 자바스크립트에 내장된 기능들과 참조를 사용해서... 해결해보자
  //     useEffect() 지우기..^^
  //     */
  //   }, []);

  //input에 나만의 메소드 활용해 해결하기
  const activate = () => {
    inputRef.current.focus();
  };

  useImperativeHandle(ref, () => {
    return { focus: activate };
  });
  //activate 함수를 input의 내부가 아닌 외부에서 호출하기 (리액트에서 흔치 않은 방식임..)

  return (
    <div
      className={`${classes.control} ${props.className} ${
        props.isValid === false ? classes.invalid : ""
      }`}
    >
      <label htmlFor={props.id}>{props.label}</label>
      <input
        type={props.type}
        id={props.id}
        value={props.value}
        onChange={props.onChange}
        onBlur={props.onBlur}
        ref={inputRef}
      >
        {props.children}
      </input>
    </div>
  );
});

export default Input;

//재사용해야 하는 컴포넌트는 props으로

/*
명령형으로 상호 작용할 수 있게 하는 훅
state 전달하여 그 컴포넌트에서 무언가를 변경하는 방식이 아닌, 컴포넌트 내부에서 함수를 호출하는 방식
이 방식은 일반적인 리액트 패턴은 아니지만 도움이 될 때도 있다.
*/
//인풋 참조 만들기
const emailInputRef = useRef();
const passwordInputRef = useRef();
//submit 핸들러
const submitHandler = (event) => {
  event.preventDefault();
  
  //폼이 유효할 때만 로그인 호출
  if (formIsValid) {
    ctx.onLogin(emailState.value, passwordState.value);
    
  } else if (!emailIsValid) {
    // 아닌 경우, 첫 번째로 이메일 인풋 유효한지 체크하기 위해 포커스
    //워매 이게 뭐람 내가 만든 액티베이트 메소드를 가져올수 있쟈나....ㅇ0ㅇ..?
    emailInputRef.current.focus();
    
  } else if (!passwordIsValid) {
    // 아닌 경우, 첫 번째로 패스워드 인풋 유효한지 체크하기 위해 포커스
    passwordInputRef.current.focus();
  }
};
//...
return (
  <Card className={classes.login}>
    <form onSubmit={submitHandler}>
      <Input
        isValid={emailIsValid}
        label="E-mail"
        type="email"
        id="email"
        value={emailState.value}
        onChange={emailChangeHandler}
        onBlur={validateEmailHandler}
        //
        ref={emailInputRef}
        />
      <Input
        isValid={passwordIsValid}
        label="Password"
        type="password"
        id="password"
        value={passwordState.value}
        onChange={passwordChangeHandler}
        onBlur={validatePasswordHandler}
        //
        ref={passwordInputRef}
        />
      <div className={classes.actions}>
        <Button type="submit" className={classes.btn}>
          Login
        </Button>
      </div>
    </form>
  </Card>
);
profile
Always have hope🍀 & constant passion🔥

0개의 댓글