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 placeholder="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>
);
});