12. ref 속성값으로 자식 요소에 접근하기
리액트로 작업하다보면 실제 돔 요소에 직접 접근해야할 때가 있다.
예를 들어, 돔 요소에 포커스를 주거나 돔 요소 크기나 스크롤 위치를 알아야 하는 경우
ref
속성값을 이용하면 자식 요소에 직접 접근할 수 있다. 여기에서 자식 요소는 돔 요소나 컴포넌트일 수 있다.
1) 돔 요소에 접근하기
ref
속성값은 아래와 같이 일반적인 컴포넌트에도 입력할 수 있다.export default function App() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);
return (
<input type="text" ref={inputRef} />
)
}
2) 컴포넌트 접근하기
<Box ref={inputRef} />
만약에 이 컴포넌트가 클래스형 컴포넌트라면 해당 컴포넌트의 인스턴스를 가리킨다. 따라서 current
속성은 해당 클래스의 메서드를 호출할 수 있게 된다.
함수형 컴포넌트는 인스턴스로 만들어지지 않지만 useImperativeHandle
이라는 훅을 사용하면 함수형 컴포넌트에서도 마치 클래스형 컴포넌트의 멤버 변수나 메서드에 접근하는 것처럼 함수형 컴포넌트의 변수나 함수를 외부로 노출시킬 수 있다.
주의: 별다른 처리를 하지 않았다면 함수형 컴포넌트에 ref
속성을 입력할 수 없고 아래와 같이 별도의 속성명을 사용하거나 forwardRef 를 사용해야 한다.
export default function App() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);
return (
<div>
<InputAndSave inputRef={inputRef} />
...
</div>
)
}
function InputAndSave({ inputRef }) {
return (
<div>
<input type="text" ref={inputRef} />
...
</div>
)
}
- forwardRef 사용하기 : 두 번째 매개변수로 ref 속성값을 받을 수 있다.
export default function App() {
const buttonRef = useRef();
return (
<div>
<Button ref={buttonRef} />
...
</div>
)
}
const Button = React.forwardRef(function ({ onClick }, ref) {
return (
<button onClick={onClick} ref={ref}>저장</button>
)
})
3) ref 속성값에 함수 입력하기
ref
속성값에 함수를 입력할 수 있다. ref
속성값에 입력된 함수는 해당하는 요소가 생성되거나 사라질 때 한 번씩 호출된다.null
값이 넘어온다.setText(INITIAL_TEXT)
를 실행하여 text
를 초기값으로 설정한다.showText
를 보였다가 가렸다가를 반복하게 되는데, input
태그는 showText
가 true
일 때만 보여지게 되므로 버튼을 클릭하여 showText
가 true
가 되는 순간 setText(INITIAL_TEXT)
함수가 호출된다.setText
함수가 실행되고 컴포넌트가 다시 렌더링이 된다. 그런데 컴포넌트가 렌더링될 때마다 새로운 함수가 입력되면서 setText(INITIAL_TEXT)
가 실행되어 초기화되기 때문에 text
가 입력하는 텍스트로 제대로 업데이트 되지 않는 문제가 있다.export default fuction App() {
const [text, setText] = useState(INITIAL_TEXT)
const [showText, setShowText] = useState(true)
return(
<div>
{showText && (
<input type="text" ref={ref => ref && setText(INITIAL_TEXT}} value={text} onChange={e => setText(e.target.value} />
)}
<button onClick ={() => setShowText(!showText)}> 보이기 / 가리기 </button>
</div>
)
}
const INITIAL_TEXT = 'Hello world!'
4) useCallback 훅을 사용하여 함수를 고정하기
useCallback
훅의 메모이제이션 기능 덕분에 한 번 생성된 함수를 변화시키지 않고 계속해서 재사용할 수 있다.setText
함수가 실행되고 컴포넌트가 다시 렌더링 되어도 setText(INITIAL_TEXT)
가 다시 실행되지 않는다.ref
가 새로 생성될 때 setText(INITIAL_TEXT)
가 실행된다.export default function App() {
const [text, setText] = useState(INITIAL_TEXT)
const [showText, setShowText] = useState(true)
const setInitialText = useCallback((ref) => ref && setText(INITIAL_TEXT)}, []);
return(
<div>
{showText && (
<input
type="text"
ref={setInitialText}
value={text}
onChange={e => setText(e.target.value)}
/>
)}
<button onClick={() => setShowText(!showText)}>보이기/가리기</button>
</div>
)
}
const INITIAL_TEXT = 'Hello world!';