forwardRef
forwardRef
๋ฅผ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ๊ฐ ref.๋ฅผ ์ฌ์ฉํ์ฌ ๋ถ๋ชจ ์ปดํฌ๋ํธ์ DOM ๋
ธ๋๋ฅผ ๋
ธ์ถํ ์ ์์ต๋๋ค.
const SomeComponent = forwardRef(render)
๋ ํผ๋ฐ์ค
forwardRef(render)
์ปดํฌ๋ํธ๊ฐ ref๋ฅผ ๋ฐ์ ํ์ ์ปดํฌ๋ํธ๋ก ์ ๋ฌํ๋๋ก ํ๋ ค๋ฉด forwardRef()
๋ฅผ ํธ์ถํ์ธ์.
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
// ...
});
์๋์์ ๋ ๋ง์ ์์๋ฅผ ํ์ธํ์ธ์.
๋งค๊ฐ๋ณ์
render
: ์ปดํฌ๋ํธ์ ๋ ๋๋ง ํจ์์ ๋๋ค. React๋ ์ปดํฌ๋ํธ๊ฐ ๋ถ๋ชจ๋ก๋ถํฐ ๋ฐ์ props์ref
๋ก ์ด ํจ์๋ฅผ ํธ์ถํฉ๋๋ค. ๋ฐํํ๋ JSX๋ ์ปดํฌ๋ํธ์ ๊ฒฐ๊ณผ๊ฐ ๋ฉ๋๋ค.
๋ฐํ๊ฐ
forwardRef
๋ JSX์์ ๋ ๋๋งํ ์ ์๋ React ์ปดํฌ๋ํธ๋ฅผ ๋ฐํํฉ๋๋ค. ์ผ๋ฐ ํจ์๋ก ์ ์๋ React ์ปดํฌ๋ํธ์ ๋ค๋ฅด๊ฒ, forwardRef
๊ฐ ๋ฐํํ๋ ์ปดํฌ๋ํธ๋ ref
prop๋ ๋ฐ์ ์ ์์ต๋๋ค.
์ฃผ์
- Strict Mode์์ React๋ ์ค์๋ก ๋ฐ์ํ ๊ฒฐํจ์ ์ฐพ๊ธฐ ์ํด ๋ ๋๋ง ํจ์๋ฅผ ๋ ๋ฒ ํธ์ถํฉ๋๋ค. ์ด๋ ๊ฐ๋ฐ ํ๊ฒฝ ์ ์ฉ ๋์์ด๋ฉฐ ํ๋ก๋์ ํ๊ฒฝ์๋ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค. ๋ ๋๋ง ํจ์๊ฐ ์์ํจ์์ธ ๊ฒฝ์ฐ(๊ทธ๋์ผ๋ง ํฉ๋๋ค.) ์ปดํฌ๋ํธ ๋ก์ง์ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค. ํธ์ถ ๊ฒฐ๊ณผ ์ค ํ๋์ ๊ฒฐ๊ณผ๋ ๋ฌด์๋ฉ๋๋ค.
render
ํจ์
forwardRef
๋ render ํจ์๋ฅผ ์ธ์๋ก ๋ฐ์ต๋๋ค. React๋ props
์ ref
์ ํจ๊ป ์ด ํจ์๋ฅผ ํธ์ถํฉ๋๋ค.
const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
});
๋งค๊ฐ๋ณ์
-
props
: ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์ ๋ฌํ props์ ๋๋ค. -
ref
: ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์ ๋ฌํref
์ดํธ๋ฆฌ๋ทฐํธ์ ๋๋ค.ref
๋ ๊ฐ์ฒด๋ ํจ์์ผ ์ ์์ต๋๋ค. ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ref๋ฅผ ์ ๋ฌํ์ง ์์ ๊ฒฝ์ฐnull
์ด ๋ฉ๋๋ค. ์ ๋ฌ๋ฐ์ref
๋ฅผ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์ ๋ฌํ๊ฑฐ๋useImperativeHandle
.์ ์ ๋ฌํด์ผ ํฉ๋๋ค.
๋ฐํ๊ฐ
forwardRef
๋ JSX์์ ๋ ๋๋งํ ์ ์๋ React ์ปดํฌ๋ํธ๋ฅผ ๋ฐํํฉ๋๋ค. ์ผ๋ฐ ํจ์๋ก ์ ์๋ React ์ปดํฌ๋ํธ์ ๋ค๋ฅด๊ฒ forwardRef
์ ์ํด ๋ฐํ๋๋ ์ปดํฌ๋ํธ๋ ref
prop๋ฅผ ๋ฐ์ ์ ์์ต๋๋ค.
์ฌ์ฉ๋ฒ
๋ถ๋ชจ ์ปดํฌ๋ํธ์ DOM ๋ ธ๋ ๋ ธ์ถํ๊ธฐ
๊ธฐ๋ณธ์ ์ผ๋ก ๊ฐ ์ปดํฌ๋ํธ์ DOM ๋
ธ๋๋ ๋น๊ณต๊ฐ์
๋๋ค. ๊ทธ๋ฌ๋ ๋๋ก๋ ๋ถ๋ชจ์ DOM ๋
ธ๋๋ฅผ ๋
ธ์ถํ๋ ๊ฒ์ด ์ ์ฉํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด focus ํ๊ธฐ ์ํด ๋
ธ์ถํ ์ ์์ต๋๋ค. ์ด๋ฅผ ์ํด ์ปดํฌ๋ํธ ์ ์๋ฅผ forwardRef()
๋ก ๊ฐ์ธ์ฃผ๋ฉด ๋ฉ๋๋ค.
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} />
</label>
);
});
props ๋ค์์ ๋ ๋ฒ์งธ ์ธ์๋ก ref๋ฅผ ๋ฐ๊ฒ ๋ฉ๋๋ค. ๋ ธ์ถํ๋ ค๋ DOM ๋ ธ๋์ ์ด๋ฅผ ์ ๋ฌํฉ๋๋ค.
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});
์ด๋ ๊ฒ ํ๋ฉด ๋ถ๋ชจ์ธ Form
์ปดํฌ๋ํธ๊ฐ MyInput
์ ์ํด ๋
ธ์ถ๋ <input>
DOM ๋
ธ๋์ ์ ๊ทผํ ์ ์์ต๋๋ค.
function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<MyInput label="Enter your name:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}
์ด Form
์ปดํฌ๋ํธ๋ MyInput
์๊ฒ ref๋ฅผ ์ ๋ฌํฉ๋๋ค. MyInput
์ปดํฌ๋ํธ๋ ํด๋น ref๋ฅผ <input>
ํ๊ทธ์ ์ ๋ฌํฉ๋๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก Form
์ปดํฌ๋ํธ๋ ํด๋น <input>
DOM ๋
ธ๋์ ์ ๊ทผํ์ฌ focus()
๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
์ปดํฌ๋ํธ ๋ด๋ถ์ DOM ๋ ธ๋์ ref๋ฅผ ๋ ธ์ถํ๋ฉด ๋์ค์ ์ปดํฌ๋ํธ์ ๋ด๋ถ๋ฅผ ๋ณ๊ฒฝํ๊ธฐ๊ฐ ๋ ์ด๋ ค์์ง๋ค๋ ์ ์ ์ ์ํ์ธ์. ์ผ๋ฐ์ ์ผ๋ก ๋ฒํผ์ด๋ ํ ์คํธ input๊ณผ ๊ฐ์ด ์ฌ์ฌ์ฉํ ์ ์๋ ์ ์์ค ์ปดํฌ๋ํธ์์ DOM ๋ ธ๋๋ฅผ ๋ ธ์ถํ์ง๋ง, ์๋ฐํ๋ ๋๊ธ ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ์ ์ปดํฌ๋ํธ์์๋ ๋ ธ์ถํ๊ณ ์ถ์ง ์์ ๊ฒ์ ๋๋ค.
์์ 1 of 2: ํ
์คํธ input์ ์ด์ ๋ง์ถ๊ธฐ
๋ฒํผ์ ํด๋ฆญํ๋ฉด input์ ํฌ์ปค์ค ๋ฉ๋๋ค. Form
์ปดํฌ๋ํธ๋ ref๋ฅผ ์ ์ํ๊ณ ์ด๋ฅผ MyInput
์ปดํฌ๋ํธ๋ก ์ ๋ฌํฉ๋๋ค. MyInput
์ปดํฌ๋ํธ๋ ํด๋น ref๋ฅผ input
์ผ๋ก ์ ๋ฌํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด Form
์ปดํฌ๋ํธ๊ฐ input
์ ํฌ์ปค์ค๋ฅผ ์ค ์ ์์ต๋๋ค.
import { useRef } from 'react'; import MyInput from './MyInput.js'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); } return ( <form> <MyInput label="Enter your name:" ref={ref} /> <button type="button" onClick={handleClick}> Edit </button> </form> ); }
์ฌ๋ฌ ์ปดํฌ๋ํธ๋ฅผ ํตํด ref ์ ๋ฌํ๊ธฐ
ref
๋ฅผ DOM ๋
ธ๋๋ก ์ ๋ฌํ์ง ์๊ณ MyInput
๊ณผ ๊ฐ์ ์ปดํฌ๋ํธ๋ก ์ ๋ฌํ ์ ์์ต๋๋ค.
const FormField = forwardRef(function FormField(props, ref) {
// ...
return (
<>
<MyInput ref={ref} />
...
</>
);
});
MyInput
์ปดํฌ๋ํธ๊ฐ <input>
์ ref๋ฅผ ์ ๋ฌํ๋ฉด FormField
์ ref๋ ํด๋น <input>
์ ์ป์ ์ ์์ต๋๋ค.
function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<FormField label="Enter your name:" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}
Form
์ปดํฌ๋ํธ๋ ref๋ฅผ ์ ์ํ๊ณ ์ด๋ฅผ FormField
์ ์ ๋ฌํฉ๋๋ค. FormField
์ปดํฌ๋ํธ๋ ํด๋น ref๋ฅผ MyInput
์ผ๋ก ์ ๋ฌํ๊ณ , ์ด ์ปดํฌ๋ํธ๋ <input>
DOM ๋
ธ๋๋ก ์ ๋ฌํฉ๋๋ค. ์ด๊ฒ์ด Form
์ด <input>
DOM ๋
ธ๋์ ์ ๊ทผํ๋ ๋ฐฉ์์
๋๋ค.
import { useRef } from 'react'; import FormField from './FormField.js'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); } return ( <form> <FormField label="Enter your name:" ref={ref} isRequired={true} /> <button type="button" onClick={handleClick}> Edit </button> </form> ); }
DOM ๋ ธ๋ ๋์ ๋ช ๋ นํ ํธ๋ค ๋ ธ์ถํ๊ธฐ
์ ์ฒด DOM ๋ ธ๋๋ฅผ ๋ ธ์ถํ๋ ๋์ ์ ํ๋ ๋ฉ์๋ ์งํฉ๊ณผ ํจ๊ป ๋ช ๋ นํ ํธ๋ค์ด๋ผ๊ณ ํ๋ ์ฌ์ฉ์ ์ ์ ๊ฐ์ฒด๋ฅผ ๋ ธ์ถํ ์ ์์ต๋๋ค. ์ด๋ฅผ ์ํด DOM ๋ ธ๋๋ฅผ ๋ณด์ ํ ๋ณ๋์ ref๋ฅผ ์ ์ํด์ผ ํฉ๋๋ค.
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
// ...
return <input {...props} ref={inputRef} />;
});
์ ๋ฌ๋ฐ์ ref
๋ฅผ useImperativeHandle
์ ์ ๋ฌํ๊ณ ๋
ธ์ถํ๋ ค๋ ๊ฐ์ ref
์ ์ง์ ํฉ๋๋ค.
import { forwardRef, useRef, useImperativeHandle } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);
return <input {...props} ref={inputRef} />;
});
์ผ๋ถ ์ปดํฌ๋ํธ๊ฐ MyInput
์ ref๋ฅผ ๋ฐ์ผ๋ฉด DOM ๋
ธ๋ ๋์ { focus, scrollIntoView }
๊ฐ์ฒด๋ง ๋ฐ์ต๋๋ค. ์ด๋ฅผ ํตํด ๋
ธ์ถํ๋ DOM ๋
ธ๋์ ์ ๋ณด๋ฅผ ์ต์ํ์ผ๋ก ์ ํํ ์ ์์ต๋๋ค.
import { useRef } from 'react'; import MyInput from './MyInput.js'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); // This won't work because the DOM node isn't exposed: // ref.current.style.opacity = 0.5; } return ( <form> <MyInput label="Enter your name:" ref={ref} /> <button type="button" onClick={handleClick}> Edit </button> </form> ); }
๋ช ๋ นํ ํธ๋ค ์ฌ์ฉ์ ๋ํด ์์ธํ ์์๋ณด์ธ์.
๋ฌธ์ ํด๊ฒฐ
์ปดํฌ๋ํธ๊ฐ forwardRef
๋ก ๊ฐ์ธ์ ธ ์์ง๋ง, ์ปดํฌ๋ํธ์ ref
๋ ํญ์ null
์
๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ref
๋ฅผ ์ค์ ๋ก ์ฌ์ฉํ๋ ๊ฒ์ ์์ด๋ฒ๋ ธ๋ค๋ ๊ฒ์
๋๋ค.
์๋ฅผ ๋ค์ด ์ด ์ปดํฌ๋ํธ๋ ref
๋ก ์๋ฌด๊ฒ๋ ํ์ง ์์ต๋๋ค.
const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input />
</label>
);
});
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ref
๋ฅผ DOM ๋
ธ๋๋ ref๋ฅผ ๋ฐ์ ์ ์๋ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์ ๋ฌํ์ธ์.
const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
});
์ผ๋ถ ๋ก์ง์ด ์กฐ๊ฑด๋ถ์ธ ๊ฒฝ์ฐ MyInput
์ ref
๊ฐ null
์ผ ์ ์์ต๋๋ค.
const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
{showInput && <input ref={ref} />}
</label>
);
});
showInput
์ด false
์ด๋ฉด ref๊ฐ ์ด๋ค ๋
ธ๋๋ก๋ ์ ๋ฌ๋์ง ์์ผ๋ฉฐ MyInput
์ ref๋ ๋น์ด ์๊ฒ ๋ฉ๋๋ค. ์ด ์์ ์ Panel
๊ณผ ๊ฐ์ด ์กฐ๊ฑด์ด ๋ค๋ฅธ ์ปดํฌ๋ํธ ์์ ์จ๊ฒจ์ ธ ์๋ ๊ฒฝ์ฐ ํนํ ์ด ์ ์ ๋์น๊ธฐ ์ฝ์ต๋๋ค.
const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
<Panel isExpanded={showInput}>
<input ref={ref} />
</Panel>
</label>
);
});