μλ‘κ² μκ² λ λ΄μ©μ΄κ±°λ μ΄ν΄νκΈ° μ΄λ €μ λ λΆλΆλ§ μ 리
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")
);