• [React] useImperativeHandle :: 마이구미
    React 2022. 4. 27. 22:05
    반응형
    이 글은 React 에서 제공해주는 Hooks API 에서 useImperativeHandle 을 다룬다.
    useRef, forwardRef 와 관련이 있다.
    example - https://codesandbox.io/s/xm605q
    docs - https://reactjs.org/docs/hooks-reference.html#useimperativehandle

    사실 useImperativeHandle 은 사용하는 경우가 많이 없어서 익숙하지 않은 훅이다.

    공식 문서에서는 다음과 같이 설명한다.

    useImperativeHandle customizes the instance value that is exposed to parent components when using ref...
    useImperativeHandle should be used with forwardRef:

     

    해석하고자하면,

    • ref 를 사용할 때 부모 컴포넌트에 노출되는 인스턴스 값을 커스텀한다?
    • forwardRef 와 함께 사용된다?

     

    바로 이해하기에는 쉽지않다.

    이것을 이해하기위해서는 useRef, forwarRef 를 우선 인지해야한다.

     

    위 내용을 쉽게 이해하기 위해 예제를 통해 진행해본다.

    예제는 요구사항 3가지를 순차적으로 해결해나간다.

    1. 컴포넌트 내부에 존재하는 input 요소에 접근해서 focus() 를 호출한다.
    2. 부모 컴포넌트에서 자식 컴포넌트의 input 요소에 접근해서 focus() 를 호출한다. (1번의 행위 주체를 부모로 전환)
    3. input 요소에 접근해서 focus() 를 호출하는 행위를 부모, 자식 모두 호출할 수 있게 한다.

     

    첫번째 요구 사항은 컴포넌트 내부에 있는 input 태그를 직접 접근해서 특정 시점에 focus() 를 호출하는것이다. 

     

    const Child = (props) => {
      const inputRef = useRef();
      
      useEffect(() => {
        inputRef.current.focus();
      }, [deps])
      
      return (
        <div>
          ...
          <input ref={ref} />
        </div>
      )
    }

     

    위 코드처럼 useRef 를 활용해서 자기자신의 컴포넌트 내부에서는 useRef 를 통해 직접 DOM 에 접근해서 focus() 를 호출하면 된다.

     

    두번째 요구사항은 부모 컴포넌트에서 자식 컴포넌트 내부에 있는 input 태그를 직접 접근해서 특정 시점에 focus() 를 호출하는것이다.

    기본적으로 부모 컴포넌트에서 자식 컴포넌트의 특정 DOM 에 접근할 수 없다는 것을 알고 있다.

    이와 같은 상황에서 자식 컴포넌트에서 forwardRef 를 활용할 수 있다.

     

    const Child = forwardRef((props, ref)) => {
      return (
        <div>
          ...
          <input ref={ref} />
        </div>
      )
    })

     

    자식 컴포넌트에서는 DOM 에 접근하기 위해 사용했던 useRef 코드가 사라졌다.

    그리고 forwardRef 로 컴포넌트가 감싸지고, ref 전달인자를 input 태그에 주입하게 된다.

     

    부모 컴포넌트에서는 자식 컴포넌트의 props 로 ref 를 전달해주게 된다.

     

    const Parent = () => {
      const inputRef = useRef();
      
      useEffect(() => {
        inputRef.current.focus();
      }, [deps])
    
      return <Child ref={inputRef} />
    }

     

    결과적으로 부모에서 원하는 시점에 자식 컴포넌트의 특정 DOM 에 직접 접근해서 focus() 를 호출할 수 있게 된다.

     

    세번째 요구사항은 첫번째, 두번째 요구사항을 합치는 것이다.

    첫번째에서는 자식 스스로 본인이 가지고 있는 DOM 에 접근했다면, 두번째는 부모가 자식의 DOM 에 직접 접근할 수 있었다.

    세번째는 부모, 자식 모두 같은 DOM 을 접근할 수 있게 하는 것이다.

     

    첫번째에서는 useRef, 두번째에서는 forwardRef 를 통해 각 요구사항을 해결했다.

    세번째 요구사항은 그대로 두개를 합치면 되니 useRef 도 쓰고, forwardRef 쓰면 될 것 같다.

     

    const Child = forwardRef((props, ref)) => {
      const inputRef = useRef();
      return (
        <div>
          ...
          <input ref={[ref, inputRef]} />
        </div>
      )
    })

     

    부모를 위해 forwardRef 를 사용하면서 자식에서도 useRef 를 통해 접근할 수 있게 하는 것이다.

    하지만 당연히 위 코드는 실행되지않는다.

     

    이러한 경우에 활용할 수 있는 것이 useImperativeHandle 훅이다.

     

    const Child = forwardRef((props, ref)) => {
      const inputRef = useRef();
      useImperativeHandle(ref, () => inputRef.current);
    
      return (
        <div>
          ...
          <input ref={inputRef} />
        </div>
      )
    })

     

    "ref 를 사용할 때 부모 컴포넌트에 노출되는 인스턴스 값을 커스텀한다?" 라는 의미가 이것이다.

    useImperativeHandle 을 통해 부모에서 사용하는 ref 를 inputRef.current 로 반환하는 것이다.

    결과적으로 부모쪽에서는 ref 값은 자식에서 반환하는 inputRef.current 로 커스텀된다는 의미가 된다.

     

     

     

     

    처음에도 언급했듯이, 이 훅을 사용하는 경우는 흔치 않다.

    그렇다고 아예 맞이할 상황이 없다는 것은 아니다.

    본인의 경우에는 폼 작업에서 react-hook-form 의 도움을 얻고 있다.

    react-hook-form 은 uncontrolled component 형태로써, ref 를 많이 활용한다.

    form 관련 컴포넌트를 구현하는 과정에서 useImperativeHandle 의 니즈를 맞이했다.

    반응형

    댓글

Designed by Tistory.