React forwardRefs와 useImperativeHandle
어떤 state를 전달해서 그 컴포넌트에서 무언가를 변경하는 방식이 아니라 자식 컴포넌트 내부에서 함수를 호출하는 방식으로 부모 컴포넌트와 자식 컴포넌트가 상호작용할 수 있다.
일반적인 리액트 패턴이 아니기때문에 자주 사용되지않고 자주 사용해서도 안되지만 도움이 될 때도 있다.
특히 포커스나 스크롤링같은 사례에서는매우 유용할 수 있다.
로그인 폼에서 유효하지 않은 input을 포커스하고 싶을 때 일반 input이라면 useRef를 사용하고 focus 메소드를 호출하면 된다.
const emailInputRef = useRef();
const passwordInputRef = useRef();
const submitHandler = (event) => {
event.preventDefault();
if (formIsValid) {
onLogin(emailState.value, passwordState.value);
} else if (!emailIsValid) {
emailInputRef.current.focus();
} else {
passwordInputRef.current.focus();
}
};
return (
<form onSubmit={submitHandler}>
<input
ref={emailInputRef}
id="email"
label="email"
type="email"
isValid={emailIsValid}
value={emailState.value}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>
<input
ref={passwordInputRef}
id="password"
label="password"
type="password"
isValid={passwordIsValid}
value={passwordState.value}
onChange={passwordChangeHandler}
onBlur={validatePasswordHandler}
/>
<div>
<Button type="submit">
Login
</Button>
</div>
</form>
);
하지만 사용자가 정의한 Input 컴포넌트가 있고 그 안에서 input 요소와 focus를 위한 activate 메소드를 조작해서 포커스를 하고 싶다면 ref만으로는 해결 할 수 없다.
사용자가 정의한 Input 컴포넌트는 ref를 받을 수 없고 ref prop을 가지고 내부적으로 아무것도 하지 않는다.
따라서 이렇게 접근하면 안되고 작동시키기 위해서 2가지가 필요하다.
첫 번째는 Input 컴포넌트에서 useImperativeHandle을 import해서 사용하는 것, 두 번째는 forwardRef를 사용하는 것이다.
useImperativeHandle 및 forwardRef를 사용하면 컴포넌트에서 기능을 노출하여 부모 컴포넌트에 연결한 다음, 부모 컴포넌트 안에서 참조를 통해 그 컴포넌트를 사용하고 기능을 트리거할 수 있다.
const Input = (props, ref) => {
const inputRef = useRef();
const activate = () => {
inputRef.current.focus();
};
useImperativeHandle(ref, () => {
return {
focus: activate,
};
});
return (
<div>
<label htmlFor={props.id}>{props.label}</label>
<input
ref={inputRef}
type={props.type}
id={props.id}
value={props.value}
onChange={props.onChange}
onBlur={props.onBlur}
/>
</div>
);
}
export default Input;
useImperativeHandle의 첫 번째 인자는 외부에서 설정한 ref이다.
부모 컴포넌트에서 ref prop을 추가하고 이것이 연결을 설정한다.
useImperativeHandle의 두 번째 인자는 객체를 반환하는 함수이다.
그 객체는 외부에서 사용할 수 있는 모든 데이터를 포함한다.
내부 함수 또는 내부 변수 또는 무엇이든 그 이름을 통해 외부에서 접근할 수 있다.
const Input = React.forwardRef((props, ref) => {
const inputRef = useRef();
const activate = () => {
inputRef.current.focus();
};
useImperativeHandle(ref, () => {
return {
focus: activate,
};
});
return (
<div>
<label htmlFor={props.id}>{props.label}</label>
<input
ref={inputRef}
type={props.type}
id={props.id}
value={props.value}
onChange={props.onChange}
onBlur={props.onBlur}
/>
</div>
);
});
export default Input;
이것 만으로는 작동하지 않고 부모컴포넌트에서 ref를 넘겨주기 위해서 forwardRef을 사용한다.
컴포넌트 함수는 forwardRef의 첫 번째 인수이고 forwardRef는 리액트 컴포넌트를 반환 한다.
따라서 Input은 여전히 리액트 컴포넌트이지만 ref에 바인딩 될 수 있는 리액트 컴포넌트이다.
'Frontend > React' 카테고리의 다른 글
React memo 개념 및 사용 방법 (0) | 2023.01.30 |
---|---|
리액트가 작동하는 방식 (0) | 2023.01.29 |
React useReducer 개념 및 사용 방법 (0) | 2023.01.25 |
React Side Effects와 useEffect (0) | 2023.01.24 |
React useRef 개념 및 사용법 (2) | 2023.01.22 |
댓글