์๋กญ๊ฒ ์๊ฒ ๋ ๋ด์ฉ์ด๊ฑฐ๋ ์ดํดํ๊ธฐ ์ด๋ ค์ ๋ ๋ถ๋ถ๋ง ์ ๋ฆฌ
React: ์ ์ด ์ปดํฌ๋ํธ์ ๋น์ ์ด ์ปดํฌ๋ํธ์ ์ฐจ์ด์ ์ฐธ๊ณ
์ ์ด ์ปดํฌ๋ํธ
์์ ํผ ๋ฐ์ดํฐ๋ React ์ปดํฌ๋ํธ์์ ๋ค๋ฃจ์ด์ง๊ณ , ์ปดํฌ๋ํธ ๋ด๋ถ ๋ก์ง์ ์ํด์ ํ๋ฉด์ ๋ณด์ฌ์ง๋ ๊ฐ์ด ๋ณํ๋ค.
๋ฐ๋ฉด์, ๋น์ ์ด ์ปดํฌ๋ํธ
์์๋ ํผ ๋ฐ์ดํฐ๋ DOM ์์ฒด์์ ๋ค๋ฃจ์ด์ง๊ณ , ์ฌ์ฉ์์ ์ํด ํ๋ฉด์ ๋ณด์ฌ์ง๋ ๊ฐ์ด ๋ณํ๋ค.
ref๋ render ๋ฉ์๋์์ ์์ฑ๋ 'DOM ๋
ธ๋'๋ 'React ์๋ฆฌ๋จผํธ'์ ์ ๊ทผ
ํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก React์ ๋ฐ์ดํฐ ํ๋ก์ฐ์์ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์์ ์ปดํฌ๋ํธ๋ฅผ ์์ ํ๋ ค๋ฉด ์๋ก์ด props๋ฅผ ์ ๋ฌํ์ฌ ์์ ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํด์ผ ํ๋ค.
๊ทธ๋ฌ๋, ref๋ฅผ ์ฌ์ฉํ๋ฉด ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์์ ์ปดํฌ๋ํธ๋ฅผ ์ง์ ์์
ํ ์ ์๋ค.
ref๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ํด๊ฒฐ๋ ์ ์๋ค๋ฉด ref๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ง์๋๋ค.
ref๋ ๋ค์๊ณผ ๊ฐ์ ์ํฉ์์ ์ฌ์ฉ๋ ์ ์๋ค.
DOM ์๋ฆฌ๋จผํธ์ Ref ์ฌ์ฉํ๊ธฐ
button input์ ํด๋ฆญํ๋ฉด text input์ด focus ๋๋๋ก ํ๋ ค๊ณ ํ๋ค.
render ๋ฉ์๋์์ ์์ฑ๋ DOM ์๋ฆฌ๋จผํธ์ ์ ๊ทผํ๋ ค๋ ๊ฒ์ด๋ค.
๋ค์๊ณผ ๊ฐ์ด ref ์ดํธ๋ฆฌ๋ทฐํธ๊ฐ HTML ์๋ฆฌ๋จผํธ์ ์ฐ์ธ ๊ฒฝ์ฐ
, ์์ฑ์์์ React.createRef()๋ก ์์ฑ๋ ref๋, ์์ ์ ์ ๋ฌ๋ฐ์ DOM ์๋ฆฌ๋จผํธ(๋ค์ ์์ ์ ๊ฒฝ์ฐ, input type="text")๋ฅผ current ํ๋กํผํฐ์ ๊ฐ์ผ๋ก ๋ฐ๋๋ค.
class CumstomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef(); // ์ปดํฌ๋ํธ ์ธ์คํด์ค๊ฐ ์์ฑ๋ ๋ ref๋ฅผ ๊ทธ ํ๋กํผํฐ๋ก ์ถ๊ฐ
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
this.textInput.current.focus();
// console.log(this.textInput.current); // input
}
render() {
return (
<div>
<input
type="text"
ref={this.textInput} // ์ปดํฌ๋ํธ ์ธ์คํด์ค์ ์ด๋ ๊ณณ์์๋ ref์ ์ ๊ทผ ๊ฐ๋ฅ
/>
<input
type="button"
value="focus"
onClick={this.focusTextInput}
/>
</div>
);
}
}
ํด๋์ค ์ปดํฌ๋ํธ์ ref ์ฌ์ฉํ๊ธฐ
ํํธ, ์ฌ๊ธฐ์ ๋ ๋์๊ฐ button input์ ์ง์ ํด๋ฆญํ๊ธฐ๋ ์ ์ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ์งํ text input์ด ์ฆ์ ํฌ์ปค์ค ๋ฐ๋๋ก ํ๋ ค๊ณ ํ๋ค.
render ๋ฉ์๋์์ React ์ปดํฌ๋ํธ์ ์ธ์คํด์ค์ ์ ๊ทผํ๋ ค๋ ๊ฒ์ด๋ค.
๋ค์๊ณผ ๊ฐ์ด ref ์ดํธ๋ฆฌ๋ทฐํธ๊ฐ ์ปค์คํ
ํด๋์ค ์ปดํฌ๋ํธ์ ์ฐ์ธ ๊ฒฝ์ฐ
, ์์ฑ์์์ React.createRef()๋ก ์์ฑ๋ ref๋, ๋ง์ดํธ๋ ์ปดํฌ๋ํธ์ ์ธ์คํด์ค(๋ค์ ์์ ์ ๊ฒฝ์ฐ, CustomTextInput)๋ฅผ current ํ๋กํผํฐ์ ๊ฐ์ผ๋ก ๋ฐ๋๋ค.
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.focusTextInput();
// console.log(this.textInput.current); // CustomTextInput
}
render() {
return (
<CustomTextInput ref={this.textInput} />
);
}
}
ReactDOM.render(
<AutoFocusTextInput />,
document.getElementById("root")
);
ํจ์ ์ปดํฌ๋ํธ์/์์ ref ์ฌ์ฉํ๊ธฐ
(1) ๋ฐ๋ฉด์, ํจ์ ์ปดํฌ๋ํธ๋ ์ธ์คํด์ค๊ฐ ์๊ธฐ ๋๋ฌธ์ ํจ์ ์ปดํฌ๋ํธ'์๋'
ref ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
(2) ๊ทธ๋ฌ๋, ํจ์ ์ปดํฌ๋ํธ'์์'
(render ๋ฉ์๋์์ ์์ฑ๋ DOM ์๋ฆฌ๋จผํธ์ ์ ๊ทผํ๊ธฐ ์ํด) ref ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ฐ๋ฅํ๋ค.
cf. ์ด๋ ํจ์ ์ปดํฌ๋ํธ์์๋ createRef ๋์ useRef๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
ํจ์ ์ปดํฌ๋ํธ์์ createRef๋ฅผ ์ฌ์ฉํ๋ฉด setState๊ฐ ํธ์ถ๋๊ณ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋ ๋๋ง๋ค createRef๋ ๋ค์ ํธ์ถ๋์ด ref.current๊ฐ null๋ก ์ด๊ธฐํ๋๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฐ๋ผ์, ref.current์ ๊ฐ์ ์ ์งํ๊ธฐ ์ํด useRef๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
// (1)์ ๊ฒฝ์ฐ
function MyFunctionComponent() {
return <input />;
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
// ํจ์ ์ปดํฌ๋ํธ'์๋' ref ์ดํธ๋ฆฌ๋ทฐํธ ์ฌ์ฉ ๋ถ๊ฐ๋ฅ
return (
<MyFunctionComponent ref={this.textInput} />
);
}
}
// (2)์ ๊ฒฝ์ฐ
function CustomTextInput(props) {
// textInput์ ref ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ํตํด ์ ๋ฌ๋๊ธฐ ์ํด์ ์ด๊ณณ์์ ์ ์๋์ด์ผ ํจ
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} /> {/* ํจ์ ์ปดํฌ๋ํธ'์์' ref ์ดํธ๋ฆฌ๋ทฐํธ ์ฌ์ฉ ๊ฐ๋ฅ */}
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
๋ถ๋ชจ ์ปดํฌ๋ํธ์์, '์์ ์ปดํฌ๋ํธ์ DOM ์๋ฆฌ๋จผํธ'์ ์ ๊ทผ
ํ๊ณ ์ ํ ๋ ref๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
(์ ์์ ๋ค์ '์ง์ ๋ ๋๋งํ๋ DOM ์๋ฆฌ๋จผํธ' ํน์ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ '์์ ์ปดํฌ๋ํธ'์ ์ ๊ทผํ๊ธฐ ์ํด ref๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ์๋ค.)
'์์ ์ปดํฌ๋ํธ์ DOM ์๋ฆฌ๋จผํธ'์ ์ ๊ทผํ๊ธฐ ์ํด์๋ ์์ ๊ฐ์ด ์์ ์ปดํฌ๋ํธ์ ref ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ์ง์ ํ๋ ๋ฐฉ์์ ์ฌ์ฉํ ์ ์๊ธด ํ์ง๋ง
์ด๋ ์์์ ์ดํด๋ดค๋ฏ ์์ ์ปดํฌ๋ํธ์ ์ธ์คํด์ค์ DOM ์๋ฆฌ๋จผํธ๊ฐ ์๋๋ผ ์์ ์ปดํฌ๋ํธ์ ์ธ์คํด์ค๋ฅผ ๊ฐ์ ธ์จ๋ค๋ ์ ์์, ๊ทธ๋ฆฌ๊ณ ์์ ์ปดํฌ๋ํธ๊ฐ ํจ์ ์ปดํฌ๋ํธ์ธ ๊ฒฝ์ฐ์๋ ์์ ์ปดํฌ๋ํธ์ ์ฌ์ฉํ ์ ์๋๋ค๋ ์ ์์, ์ข์ ๋ฐฉ๋ฒ์ด ์๋๋ค.
๋์ ์ (React 16.3 ์ดํ ๋ฒ์ ์ React๋ฅผ ์ฌ์ฉ ์ค์ด๋ผ๋ฉด) ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ '์์ ์ปดํฌ๋ํธ์ DOM ์๋ฆฌ๋จผํธ'์ ์ ๊ทผํ๊ธฐ ์ํด ref๋ฅผ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ
์ ์ฌ์ฉํ ์ ์๋ค.
์ด๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์์ ์ปดํฌ๋ํธ์ ref๋ฅผ ์์ ์ ref๋ก์ ์ธ๋ถ์ ๋
ธ์ถ
์ํค๋ ๋ฐฉ๋ฒ์ ๋งํ๋ค.
React.forwardRef()
๋ฅผ ์ด์ฉํด ์ผ๋ถ ์ปดํฌ๋ํธ(๋ค์ ์์ ์ ๊ฒฝ์ฐ, < FancyButton />)๊ฐ ์์ ํ ref๋ฅผ ๋ฐ์ ์กฐ๊ธ ๋ ์๋๋ก ์ ๋ฌ(์ฆ, '์ ์ก')ํ ์ ์๋ ์ตํธ์ธ ๊ธฐ๋ฅ
์ด๋ค. (์ตํธ์ธ: ๋น์ฌ์๊ฐ ๊ฐ์ธ ๋ฐ์ดํฐ ์์ง์ ํ์ฉํ๊ธฐ ์ ๊น์ง ๋น์ฌ์์ ๋ฐ์ดํฐ ์์ง์ ๊ธ์งํ๋ ์ ๋)
ref ์ดํธ๋ฆฌ๋ทฐํธ๊ฐ HTML ์๋ฆฌ๋จผํธ์ ์ฐ์ธ ๊ฒฝ์ฐ, ref ๊ฐ์ฒด๋ ์์ ์ ์ ๋ฌ๋ฐ์ DOM ์๋ฆฌ๋จผํธ๋ฅผ, ๊ทธ current ํ๋กํผํฐ์ ๊ฐ์ผ๋ก ๋ฐ๋ ๊ฒ์ ์ด์ฉํ ๋ฐฉ๋ฒ์ด๋ค.
// 3. ์ด ref๋ฅผ forwardRef ๋ด๋ถ์ (props, ref) => ... ํจ์์ ๋ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ
const FancyButton = React.forwardRef((props, ref) => {
return (
<button ref={ref} className="FancyButton"> {/* 4. ์ด ref๋ฅผ <button>์ผ๋ก ์ ๋ฌ */}
{props.children}
</button>
);
});
const App = () => {
const ref = React.createRef(); // 1. createRef๋ฅผ ํธ์ถํ์ฌ ref๋ฅผ ์์ฑํ๊ณ ref ๋ณ์์ ํ ๋น
const onMouseOver = () => {
ref.current.focus();
// 5. ์ต์ด ์ปดํฌ๋ํธ ๋ ๋๋ง์ด ๋๋๋ฉด, ref.current๋ <button> DOM ๋
ธ๋๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ๋จ
// console.log(ref.current); // <button class="FancyButton">Click me!</button>
};
return (
<div>
<input onMouseOver={onMouseOver}/>
<FancyButton ref={ref}>Click me!</FancyButton> {/* 2. ref๋ฅผ <FancyButton />์ผ๋ก ์ ๋ฌ */}
</div>
);
};
ReactDOM.render(
<App/>,
document.getElementById("root")
);
ref๋ฅผ ์ค์ ํ๊ธฐ ์ํ ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ref๊ฐ ์ค์ ๋๊ณ ํด์ ๋๋ ์ํฉ์ ์ธ์ธํ๊ฒ ๋ค๋ฃฐ ์ ์๋ ์ฝ๋ฐฑ ref๊ฐ ์๋ค.
์์์ ์ดํด๋ดค๋ ๊ฒ๊ณผ๋ ๋ฌ๋ฆฌ ref ์ดํธ๋ฆฌ๋ทฐํธ
์ React.createRef()๋ฅผ ํตํด ์์ฑ๋ ref ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๋ ๋์ ์, ref ์ฝ๋ฐฑ ํจ์๋ฅผ ์ ๋ฌํ๋ค.
ref ์ฝ๋ฐฑ์ ์ธ๋ผ์ธ ํจ์๋ก ์ ์ธํ๊ธฐ๋ณด๋ค ํด๋์ค์ ๋ฐ์ธ๋ฉ๋ ๋ฉ์๋๋ก ์ ์ธ
ํ๋ ๊ฒ์ด ์ข๋ค.
ํํธ, ref๋ฅผ ์์ ํ๋ ์์
๊ณผ ref ์ฝ๋ฐฑ ๋ชจ๋ componentDidMount ๋๋ componentDidUpdate๊ฐ ํธ์ถ๋๊ธฐ ์ ์
์ํ๋๊ณ ํธ์ถ๋๋ค.
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null; // 1. DOM ์๋ฆฌ๋จผํธ(๋๋ ์์ ์ปดํฌ๋ํธ ์ธ์คํด์ค)์ ๋ํ ์ฐธ์กฐ๋ฅผ ์ ์ฅํ , ๋ถ๋ชจ ์ปดํฌ๋ํธ ์ธ์คํด์ค์ ํ๋กํผํฐ ๋ง๋ค๊ธฐ
this.setTextInputRef = element => {
this.textInput = element; // 4. ์ด๋ฅผ ๋ถ๋ชจ ์ปดํฌ๋ํธ ์ธ์คํด์ค ํ๋กํผํฐ์ ์ ์ฅํจ
// 3. ref ์ฝ๋ฐฑ์ ref ์ดํธ๋ฆฌ๋ทฐํธ๊ฐ ์ฐ์ธ DOM ์๋ฆฌ๋จผํธ(๋๋ ์์ ์ปดํฌ๋ํธ์ ์ธ์คํด์ค)๋ฅผ ์ธ์๋ก ๋ฐ์
// console.log(element); // <input type="text">
};
// ( constructor ์์ ์ ์๋์ด ์์ผ๋ฏ๋ก ๋ฐ์์ ์ด๋ฒคํธ ํธ๋ค๋ฌ ํจ์๋ก ์ฌ์ฉ๋ ๋ this ๋ฐ์ธ๋ฉ ์ํด๋ ๋๋ค )
this.focusTextInput = () => {
// 6. ์ต์ด ์ปดํฌ๋ํธ ๋ ๋๋ง ์ ์๋์ผ๋ก ํฌ์ปค์ค ๋จ, ๊ทธ ํ ๋ฒํผ ๋๋ฅผ ๋๋ง๋ค ํฌ์ปค์ค๋จ
// => render ๋ฉ์๋์ ์ํด ์์ฑ๋๋ DOM ์๋ฆฌ๋จผํธ์ ์ ๊ทผํ ์ ์๊ฒ ๋์๋ค!
if (this.textInput) {
this.textInput.focus();
}
};
} // ( ...์ฌ๊ธฐ๊น์ง๊ฐ constructor )
componentDidMount() { // 5. ์ต์ด ์ปดํฌ๋ํธ ๋ ๋๋ง์ด ๋๋ ํ componentDidMount ๋ฉ์๋๊ฐ ํธ์ถ๋จ
this.focusTextInput();
}
render() {
return (
<div>
<input
type="text"
ref={this.setTextInputRef} {/* 2. render ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด componentDidMount ํธ์ถ ์ ์ ref ์ฝ๋ฐฑ์ด ํธ์ถ๋จ */}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
ReactDOM.render(
<CustomTextInput/>,
document.getElementById("root")
);
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} /> {/* ํจ์ ์ปดํฌ๋ํธ'์์ ' ref๋ฅผ ์ฌ์ฉํ ์ ์๋ค. */}
</div>
);
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.inputElement = null;
}
inputRef(element) {
this.inputElement = element;
}
componentDidMount() {
this.inputElement.focus();
}
render() {
return (
<CustomTextInput inputRef={el => this.inputElement = el} />
{/* props ํ๋กํผํฐ๋ก inputRef(ref ์ฝ๋ฐฑ)๋ฅผ ์ ๋ฌํ๋ค.
โป ์ฃผ์! ํจ์ ์ปดํฌ๋ํธ'์๋' ref๋ ์ฌ์ฉํ ์ ์๋ค.
์ฌ๊ธฐ์๋ ref๋ฅผ ์ฌ์ฉํ ๊ฒ ์๋๋ผ inputRef๋ผ๋ prop์ ์์ฑํ ๊ฒ์ด๋ค. */}
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById("root")
);
React ๋ ๋๋ง ์๋ช
์ฃผ๊ธฐ์์ ํผ ์๋ฆฌ๋จผํธ์ value ์ดํธ๋ฆฌ๋ทฐํธ๋ DOM์ value๋ฅผ ๋์ฒดํ๋ค.
๋น์ ์ด ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ ๋ ์ด๊น๊ฐ์ ์ง์ ํด์ค ์ ์์ง๋ง, ๊ทธ ์ดํ์ ์
๋ฐ์ดํธ๋ ์ ์ดํ์ง ์๋ ๊ฒ์ด ์ข๋ค.
์ด๋ฅผ ์ํด JSX์์์ value ์ดํธ๋ฆฌ๋ทฐํธ ๋์ defaultValue ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ ๋ ํ์ defaultValue ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ๋ณ๊ฒฝํด๋ DOM์ ๊ฐ์ ์
๋ฐ์ดํธ ๋์ง ์๋๋ค.
< input type="checkbox" >์ < input type="radio" >๋ defaultChecked
๋ฅผ ์ง์ํ๊ณ , < input >๊ณผ < select >, < textarea >๋ defaultValue
๋ฅผ ์ง์ํ๋ค.
class CustomForm extends React.Component {
constructor(props) {
super(props);
this.input = React.createRef();
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
console.log(this.input.current.value);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
defaultValue="Bob"
type="text"
ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<CustomForm />,
document.getElementById("root")
);
React์์ <input type="file" />
์ ํ๋ก๊ทธ๋๋ฐ์ ์ผ๋ก ๊ฐ์ ์ค์ ํ ์ ์๊ณ ์ฌ์ฉ์๋ง์ด ๊ฐ์ ์ค์ ํ ์ ์๊ธฐ ๋๋ฌธ์ ํญ์ ๋น์ ์ด ์ปดํฌ๋ํธ
์
๋๋ค.
๋ค์ ์์ ์์๋ onSubmit ์ด๋ฒคํธ ํธ๋ค๋ฌ ํจ์์์ ํ์ผ์ ์ ๊ทผํ๊ธฐ ์ํด์ DOM ์๋ฆฌ๋จผํธ์ ref๋ฅผ ๋ง๋ค์๋ค.
const CustomForm = () => {
const fileInput = React.useRef(null);
const onSubmit = (event) => {
event.preventDefault();
console.log(fileInput.current.files[0].name);
};
return (
<div>
<form onSubmit={onSubmit}>
<label htmlFor="file">
ํ์ผ:
<input id="file" type="file" ref={fileInput} />
</label>
<input type="submit" value="submit" />
</form>
</div>
);
};
ReactDOM.render(
<CustomForm />,
document.getElementById("root")
);