우선 제어컴포넌트는 input, textarea, select와 같은 폼 엘리먼트를 사용하여 자신의 state를 관리하고 업데이트 하는 방식을 말한다.
비제어 컴포넌트는 폼엘리먼트를 이용하지 않고, 직접 DOM에서 폼을 가져와서 state를 업데이트 하는 것을 말한다.
//class component 에서의 ref
class FileInput extends React.Component{
constructor(props){
this.fileInput= React.createRef();
}
render(){
return (
<input type="file" ref={this.fileInput}/>
}
//function component에서의 ref
function FileInput(){
const fileInput=useRef();
return(
<input type="file" ref={fileInput}/>
)
}
커스텀 클래스 컴포넌트에서는 ref객체는 마운트된 컴포넌트의 인스턴스들을 current 프로퍼티의 값으로 받는다. 그래서 자식 컴포넌트의 메서드를 부모 컴포넌트에서 실행할 수 있다. 하지만, 자식컴포넌트가 함수형으로 작성되었다면 함수 컴포넌트는 인스턴스가 없기 때문에 사용할 수 없다.
자식 컴포넌트 안의 엘리먼트 등을 확인하기 위해 forwardRef(높은 확률로 useImperativeHandle을 사용)를 사용할 수 있다.
function CatParent(){
const catRef= useRef();
return(
<Cat ref={catRef}/>
)
}
const Cat = forwardRef((props,ref)=>{
const inputRef=useRef();
useImperativeHAndle(ref, ()=>({
focus:()=>{
inputRef.current.focus();
}
}));
return (
<div>
<input ref={inputRef}/>
</div>
)
}
useRef는 .current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환한다. 반환된 객체는 컴포넌트의 전 생애주기를 통해 유지될 것 이다. (==useState와 같이 사용할 수 있다.) useRef는 매번 렌더링 할 때 동일한 ref 객체를 제공하며, 내용이 변경될 때 그것을 알려주지 않는다.
function Timer(){
const intervalRef =useRef();
useEffect(()=>{
const id = setInterval(()=>{
//...
});
intervalRef.current = id;
return()=>{
clearInterval(intervalRef.current);
};
});
//...
}
clearInterval을 해야하고, 변경이 있을 때 리렌더링을 시키지 않고 싶은 위와같은 상황에서 자주 사용된다.
함수를 ref에 넘겨 주는것
콜백 ref를 사용하면 자식 컴포넌트가 나중에 측정된 노드를 표시하더라고 여전히 부모 컴포넌트에서 이에 대한 알림을 받고 측정을 업데이트 할 수 있다.
const catCallbackRef=(node)=>{
if(node!==null){
alert(node);
alert(node.getBoundingClientRect().height);
setHeight(node.getBoundingClientRect().height);
}
};
return (
<Cat ref={catCallbackRef}/>
)
//cat
const Cat = forwardRef((props,ref)=>{
return (
<h1 ref={ref}>text text</h1>
)