본문 바로가기
Frontend/React

React forwardRefs와 useImperativeHandle

by Forsaken Developer 2023. 1. 28.
728x90
728x90

React forwardRefs와 useImperativeHandle

어떤 state를 전달해서 그 컴포넌트에서 무언가를 변경하는 방식이 아니라 자식 컴포넌트 내부에서 함수를 호출하는 방식으로 부모 컴포넌트와 자식 컴포넌트가 상호작용할 수 있다.

일반적인 리액트 패턴이 아니기때문에 자주 사용되지않고 자주 사용해서도 안되지만 도움이 될 때도 있다.

특히 포커스나 스크롤링같은 사례에서는매우 유용할 수 있다.

로그인 폼에서 유효하지 않은 input을 포커스하고 싶을 때 일반 input이라면 useRef를 사용하고 focus 메소드를 호출하면 된다.

const emailInputRef = useRef();
const passwordInputRef = useRef();

const submitHandler = (event) => {
    event.preventDefault();
    if (formIsValid) {
      onLogin(emailState.value, passwordState.value);
    } else if (!emailIsValid) {
      emailInputRef.current.focus();
    } else {
      passwordInputRef.current.focus();
    }
  };

return (
      <form onSubmit={submitHandler}>
        <input
          ref={emailInputRef}
          id="email"
          label="email"
          type="email"
          isValid={emailIsValid}
          value={emailState.value}
          onChange={emailChangeHandler}
          onBlur={validateEmailHandler}
        />

        <input
          ref={passwordInputRef}
          id="password"
          label="password"
          type="password"
          isValid={passwordIsValid}
          value={passwordState.value}
          onChange={passwordChangeHandler}
          onBlur={validatePasswordHandler}
        />

        <div>
          <Button type="submit">
            Login
          </Button>
        </div>
      </form>
  );

하지만 사용자가 정의한 Input 컴포넌트가 있고 그 안에서 input 요소와 focus를 위한 activate 메소드를 조작해서 포커스를 하고 싶다면 ref만으로는 해결 할 수 없다.

사용자가 정의한 Input 컴포넌트는 ref를 받을 수 없고 ref prop을 가지고 내부적으로 아무것도 하지 않는다.

따라서 이렇게 접근하면 안되고 작동시키기 위해서 2가지가 필요하다.

첫 번째는 Input 컴포넌트에서 useImperativeHandle을 import해서 사용하는 것, 두 번째는 forwardRef를 사용하는 것이다.

useImperativeHandle 및 forwardRef를 사용하면 컴포넌트에서 기능을 노출하여 부모 컴포넌트에 연결한 다음, 부모 컴포넌트 안에서 참조를 통해 그 컴포넌트를 사용하고 기능을 트리거할 수 있다.

const Input = (props, ref) => {
  const inputRef = useRef();

  const activate = () => {
    inputRef.current.focus();
  };

  useImperativeHandle(ref, () => {
    return {
      focus: activate,
    };
  });

  return (
    <div>
      <label htmlFor={props.id}>{props.label}</label>
      <input
        ref={inputRef}
        type={props.type}
        id={props.id}
        value={props.value}
        onChange={props.onChange}
        onBlur={props.onBlur}
      />
    </div>
  );
}

export default Input;

useImperativeHandle의 첫 번째 인자는 외부에서 설정한 ref이다.

부모 컴포넌트에서 ref prop을 추가하고 이것이 연결을 설정한다.

useImperativeHandle의 두 번째 인자는 객체를 반환하는 함수이다.

그 객체는 외부에서 사용할 수 있는 모든 데이터를 포함한다.

내부 함수 또는 내부 변수 또는 무엇이든 그 이름을 통해 외부에서 접근할 수 있다.

const Input = React.forwardRef((props, ref) => {
  const inputRef = useRef();

  const activate = () => {
    inputRef.current.focus();
  };

  useImperativeHandle(ref, () => {
    return {
      focus: activate,
    };
  });
  return (
    <div>
      <label htmlFor={props.id}>{props.label}</label>
      <input
        ref={inputRef}
        type={props.type}
        id={props.id}
        value={props.value}
        onChange={props.onChange}
        onBlur={props.onBlur}
      />
    </div>
  );
});

export default Input;

이것 만으로는 작동하지 않고 부모컴포넌트에서 ref를 넘겨주기 위해서 forwardRef을 사용한다.

컴포넌트 함수는 forwardRef의 첫 번째 인수이고 forwardRef는 리액트 컴포넌트를 반환 한다.

따라서 Input은 여전히 리액트 컴포넌트이지만 ref에 바인딩 될 수 있는 리액트 컴포넌트이다.

728x90
반응형

'Frontend > React' 카테고리의 다른 글

React memo 개념 및 사용 방법  (0) 2023.01.30
리액트가 작동하는 방식  (0) 2023.01.29
React useReducer 개념 및 사용 방법  (0) 2023.01.25
React Side Effects와 useEffect  (0) 2023.01.24
React useRef 개념 및 사용법  (2) 2023.01.22

댓글