참고문서 - avoiding-use-effect-with-callback-refs
ForwardedRef 의 타입을 알아보던 중에 좋은 글을 발견해서 작성했습니다리액트 공식 문서에서는
When you want a component to “remember” some information, but you don’t want that information to trigger new renders, you can use a ref.
해당 ref에 저장되어 있는 값이 변해도, 이것이 재 렌더링을 유발하지는 않는다
이 성질을 이용하여 DOM을 직접적으로 조작 할때에 주로 사용된다
React automatically updates the DOM to match your render output, so your components won’t often need to manipulate it. However, sometimes you might need access to the DOM elements managed by React—for example, to focus a node, scroll to it, or measure its size and position. There is no built-in way to do those things in React, so you will need a ref to the DOM node.
위에서 설명했던 포커스를 자동 설정하는 가장 일반적인 코드를 한번 짜보면
const ref = useRef(null)
useEffect(() =>
ref.current?.focus(),[])
return <input ref={ref}/>
그렇다면 이 코드에서는 ?
const Form = React.forwardRef((props, ref) => {
const [show, setShow] = React.useState(false)
useEffect(() =>
ref.current?.focus(),[])
return (
<form>
<button type="button" onClick={() => setShow(true)}>
show
</button>
{show && <input ref={ref} />}
</form>
)
})
제일 처음 렌더링 되었을 때, ref.current는 null 이다.
만약 버튼을 눌러도 useEffect의 의존성 배열 원소가 없기 때문에 useEffect 안의 콜백함수는 실행되지 않을테고, focus가 생기지 않는다
show 를 넣어줄 수도 있겠지만, 만약에 ref를 외부에서 props로 전달받는다면?(ref forwarding)export default function App() {
const ref = useRef(null)
useEffect(() => {
// ref.current는 null
ref.current?.focus()
}, [])
return <Form ref={ref} />
}
const Form = forwardRef((props, ref) => {
const [show, setShow] = useState(false)
return (
<form>
<button type="button" onClick={() => setShow(!show)}>
show
</button>
{show && <input ref={ref} />}
</form>
)
})
ForwardedRef<T> 라는 타입을 자세히 보면
((instance: T | null) => void) 라는 함수 타입이 포함되어 있는 걸 알 수 있다
이 말은 ref를
<input ref={ref}/>
<input ref={(node) => ref.current = node}/>
이런 식으로 선언할 수 있다는 뜻이다
메인테이너의 말을 빌리면
I like to think about refs on React elements as functions that are called after the component has rendered. This function gets the rendered DOM node passed as argument. If the React element unmounts, it will be called once more with null.
React Element의 ref는 컴포넌트가 렌더링된 이후 실행되는 함수라고 생각한다. 이 함수는 렌더링된 DOM node를 인자로 받고, 해당 컴포넌트가 언마운트되면 한번 더 호출되어 null을 인자로 받는다
콘솔을 찍어보면 해당 컴포넌트가 마운트 될 경우 인자로 DOM node 가 찍히고, 언마운트된 후에는 null 이 찍힌다
그렇다면 굳이 useEffect 를 사용할 필요 없이
<input ref={(node) => node?.focus()}/>
로 바꿔주면 원하는대로 동작한다
위의 경우를 살펴보면, 사실 useRef를 통해 ref 객체(RefObject)를 만들 필요가 없다는 걸 알 수 있다
함수 만 있으면 된다그렇다면 코드를
const ref = (node) => node?.focus()
return <Form ref={ref} />
이런 식으로 변경할 수 있고,
해당 함수를 useCallback으로 감싸주면 렌더링 관련해서 약간의 최적화를 해줄 수도 있다
const ref = React.useCallback((node) => {
node?.focus()
}, [])
return <Form ref={ref}/>