소개
forwardRef를 통해 ref를 사용하는 부모 컴포넌트 측에서
커스터마이징된 메서드를 사용할 수 있게 해 주는 훅이다.
useImperativeHandle(ref, createHandle, [deps])
첫 번째 인자는 프로퍼티를 부여할 ref이고, 두 번째 인자는 객체를 리턴하는 함수이다.
이 객체에 추가하고 싶은 프로퍼티 & 메서드를 정의하면 된다.
예제
import Input from "./components/Input"; import { InputElement } from "./components/Input"; const App = () => { // inputRef라는 동아줄을 만들어서 자식에게 보낸다. const inputRef = useRef<InputElement>(null); const handleClickFocus = useCallback(() => { inputRef.current?.focus(); }, []); const handleClickClear = useCallback(() => { if (inputRef.current) { inputRef.current?.clear(); } }, []); return ( <div> <Input ref={inputRef} /> <button onClick={handleClickFocus}>Focus</button> <button onClick={handleClickClear}>Clear</button> </div> ); }; export default App;
import { forwardRef, useImperativeHandle, useRef } from "react"; import React from "react"; export interface InputElement extends HTMLInputElement { clear(): void; } /* 부모가 내려준 ref에다가 이것 저것 작업을 한다. 부모는 이 로직에 대해 구체적으로 몰라도, 그냥 ref.current로 접근하여 사용하면 된다. */ const Input = forwardRef((_, ref) => { const inputRef = useRef<InputElement>(null); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current?.focus(); }, clear: () => { if (inputRef.current) { inputRef.current.value = ""; } }, })); return ( <> Input: <input ref={inputRef} /> </> ); }); export default Input;
input 엘리먼트에는 clear라는 메서드가 없지만,
useImperativeHandle에서 정의했으므로 사용할 수 있다.
장점
자식 컴포넌트에 상태나 로직을 isolate할 수 있다는 점이다.
리액트에서는 데이터가 부모에서 자식으로만 흐르는데,
이렇게 단방향으로만 작동하는 방식을 피할 때에는 보통 Redux나 Context API를 사용한다.
이것들을 사용하면 상태를 끌어올리지 않고도 부모에게 변경된 데이터가 적용되게 할 수 있다.
하지만 그 정도의 일이 아닐 때, useImperativeHandle을 사용할 수 있다.
상태나 로직들은 자식 컴포넌트가 갖고 있고,
부모 컴포넌트는 ref.current에서 필요한 프로퍼티를 가져오기만 하면 되기 때문이다.
하지만 기본적으로 리액트는 ref를 사용하는 것을 권장하지 않는다.
필요한 곳에서 선별적으로 사용하는 것이 좋겠다.
2022-10-10
- forwardRef와 함께 사용해야 한다.
- 부모 컴포넌트에서 자식의 state나 함수를 참조할 수 있지만,
일대일 관계만 가능한 것으로 보인다. 초기화가 되어 버리기 때문이다.
(두 번째 인자의 함수 이름도 init이다)
기존의 ref 데이터를 유지할 수 없어서, 일대다 관계는 불가능했다.
- forwardRef로 전달받은 ref는 current를 참조할 수 없었다.
HTML 엘리먼트만 접근할 수 있기 때문일까?
(ref를 직접 수정하게 하는 것 보다는, 수정할 수 있는 함수를 주는 것이 더 낫다)