일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Carousel
- 제어컴포넌트
- ㅡ
- 화살표2개
- alias설정
- CustomHook
- 부모요소의 패딩 무시
- twoarrow
- QueryClient
- es6
- ?? #null병합연산자
- ignore padding
- BFC
- 리액트
- 부모패딩
- debouncing
- DOM
- 문제해결
- transition
- useQueryClient
- 함수형프로그래밍
- 조건부스타일
- react
- createPortal
- 서초구보건소 #무료CPR교육
- vite
- tailwindCSS
- parent padding
- accordian
- BlockFormattingContext
- Today
- Total
프론트엔드 첫걸음
useImperativeHandle 과 forwardRef 본문
/* 본인의 이해를 돕기 위해 적혀진 글입니다. 오류가 있을 수 있습니다. */
사용상황
아래와 같이 공통으로 사용할 Input 컴포넌트 만들어준 다음에 Login 컴포넌트에서 재사용하려고 한다.
Input.js
import React, { useEffect, useRef } from 'react';
import classes from './Input.module.css';
const Input = (props) => {
const inputRef = useRef();
const activate = () => {
inputRef.current.focus();
};
return (
<div
className={`${classes.control} ${
props.isValid === false ? classes.invalid : ''
}`}
>
<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;
Login.js
import React, {
useState,
useEffect,
useReducer,
useContext,
useRef,
} from 'react';
import Input from '../UI/Input/Input';
const Login = (props) => {
const emailInputRef = useRef();
const passwordInputRef = useRef();
const submitHandler = (event) => {
event.preventDefault();
if (formIsValid) {
authCtx.onLogin(emailState.value, passwordState.value);
} else if (!emailValid) {
emailInputRef.current.activate();
} else {
passwordInputRef.current.activate();
}
};
return (
<Card className={classes.login}>
<form onSubmit={submitHandler}>
<Input
ref={emailInputRef}
id='email'
label='E-mail'
type='email'
isValid={emailValid}
value={emailState.value}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>
<Input
ref={passwordInputRef}
id='password'
label='Password'
type='password'
isValid={passwordValid}
value={passwordState.value}
onChange={passwordChangeHandler}
onBlur={validatePasswordHandler}
/>
<div className={classes.actions}>
<Button type='submit' className={classes.btn}>
Login
</Button>
</div>
</form>
</Card>
);
};
export default Login;
(포스팅 목적에 맞지않는 코드들은 생략함)
이렇게 Input 컴포넌트에 Login 컴포넌트에서 만들어준 InputRef를 넘겨서
inputRef가 가리키는 dom을 focus하려고 했지만 실행되지 않는다.
props는 ref를 받을 수 없기 때문이다.
useRef가 사용하는 focus함수를 하위 컴포넌트에서 사용하게 할 수는 없을까?
이 때 useImperativeHandle 훅을 사용하면 해결된다.
useImperativeHandle 훅을 사용하면 컴포넌트나 컴포넌트 내부에서 오는 기능들을
명령적으로(Imperative) 사용할 수 있게 한다.
일반적인 state, props 상태관리로 컴포넌트를 제어하거나
부모컴포넌트의 state 통해서 컴포넌트를 제어하지 않고,
컴포넌트에서 무언가를 직접 호출하거나 조작하여 사용하게 한다.
사용방법
1. 자식컴포넌트에서 useImperativeHandle 호출 + React.forwardRef로 컴포넌트 감싸주기
useImperativeHandle의 첫번째 매개변수는 ref 로 자식컴포넌트의 ref 와 부모컴포넌트의 ref를 바인딩 시킨다.
두번째 매개변수는 객체를 반환하는 함수로 이를 활성화 하려면 컴포넌트 함수를 React.forwardRef 로 감싸줘야한다.
컴포넌트 함수 자체가 forwardRef의 첫번째 인수가 된다.
forwardRef가 반환한 리액트 컴포넌트는 ref에 바인딩 될 수 있는 리액트 컴포넌트다.
Input.js
import React, { useEffect, useRef, useImperativeHandle } from 'react';
import classes from './Input.module.css';
const Input = React.forwardRef((props, ref) => {
const inputRef = useRef();
const activate = () => {
inputRef.current.focus();
};
useImperativeHandle(ref, () => {
return {
focus: activate,
};
});
return (
<div
className={`${classes.control} ${
props.isValid === false ? classes.invalid : ''
}`}
>
<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;
이렇게 하면 이 컴포넌트를 사용하는 부모컴포넌트는 inputRef.current.focus()를 호출할 수 있다.
2. 부모컴포넌트에서 ref와 연결된 useImperativeHandle의 함수(노출되는 인스턴스값) 사용하기
const submitHandler = (event) => {
event.preventDefault();
if (formIsValid) {
authCtx.onLogin(emailState.value, passwordState.value);
} else if (!emailValid) {
emailInputRef.current.focus();
} else {
passwordInputRef.current.focus();
}
};
useImperativeHandle은 ref를 사용할 때 부모 컴포넌트에 노출되는 인스턴스 값을 사용자화(customizes)합니다.
자식컴포넌트에서 커스터마이징해서 return 한 { focus : activate } 객체를
부모컴포넌트의 inputRef가 .current.focus() 로 갖다 쓸 수 있게끔
자식컴포넌트의 ref를 부모 컴포넌트의 ref에 연결시킨다. 인 것 같다.
https://reactjs.org/docs/hooks-reference.html#useimperativehandle
'개발 공부 > React' 카테고리의 다른 글
객체 타입의 prop 전개연산자로 전달하기 (0) | 2022.07.30 |
---|---|
[문제해결] useLocation - 주소에 따라서 활성화된 메뉴 스타일 변경 (0) | 2022.07.27 |
리액트 Hook 규칙 (0) | 2022.07.26 |
useEffect - 2. clean up 함수 (0) | 2022.07.25 |
useEffect - 1. 사용 목적 (0) | 2022.07.25 |