JavaScript에서 특정 DOM을 선택해야할 경우에는 일반적으로 getElementById
와 querySelector
와 같은 DOM Selector 함수를 사용한다. 이와 같은 동작을 리액트에서는 ref
를 사용해서 구현한다. 함수형 컴포넌트에서 ref를 사용할 때는 useRef
라는 Hook 함수를 사용하고 클래스형 컴포넌트에서는 콜백함수를 사용하거나 React.createRef
라는 함수를 사용한다.
import React, { useState, useRef } from "react";
const InputSample = () => {
const [inputs, setInputs] = useState({
name: "",
nickname: "",
});
const nameInput = useRef(); // useRef()를 사용해서 Ref 객체 생성
const { name, nickname } = inputs;
const onChange = (e) => {
const { value, name } = e.target;
setInputs({
...inputs,
[name]: value,
});
};
const onReset = () => {
setInputs({
name: "",
nickname: "",
});
nameInput.current.focus(); // nameInput.current가 특정 DOM을 가리킴.ㄴ
};
return (
<div>
<input
name="name"
value={name}
placeholder="이름"
onChange={onChange}
ref={nameInput} // 특정 DOM에 ref를 지정
/>
<input
name="nickname"
value={nickname}
placeholder="닉네임"
onChange={onChange}
/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
{name} {nickname}
</div>
</div>
);
};
export default InputSample;
useRef
는 DOM을 선택하는 용도 외에 컴포넌트 내부에서 조회 및 수정할 수 있는 변수를 관리하는데도 사용된다. useRef
로 관리하는 변수는 값이 변경된다고 해도 컴포넌트가 리렌더링되지 않는다. 즉, 리액트 컴포넌트에서의 상태는 상태를 변경하는 함수를 호출하고나서 렌더링된 다음에 업데이트된 상태를 조회할 수 있지만 useRef
로 관리하고 있는 변수는 설정 후에 바로 조회할 수 있다.
// App.js
import React, { useState, useRef } from "react";
import CreactUser from "./components/CreateUser";
import UserList from "./components/UserList";
function App() {
const [inputs, setInputs] = useState({
username: "",
email: "",
});
const { username, email } = inputs;
const onChange = (e) => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value,
});
};
const [users, setUsers] = useState([
{
id: 1,
username: "Leo",
email: "jang.xmin@gmail.com",
},
{
id: 2,
username: "Ryan",
email: "Ryan@kakao.com",
},
{
id: 3,
username: "Con",
email: "Con77@naver.com",
},
]);
const nextId = useRef(4); // useRef를 변수로 사용
const onCreate = () => {
// 배열에 항목 추가
const user = {
id: nextId.current,
username,
email,
};
// 불변성을 유지하기 위해서 spread 문법을 사용하거나 concat을 사용
setUsers([...users, user]); // spread
// setUsers(users.concat(user)); // concat
setInputs({
username: "",
email: "",
});
nextId.current += 1;
};
return (
<div className="App">
<CreactUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} />
</div>
);
}
export default App;
// CreateUser.js
import React from "react";
const CreactUser = ({ username, email, onChange, onCreate }) => {
return (
<div>
<input
name="username"
placeholder="계정명"
onChange={onChange}
value={username}
/>
<input
name="email"
placeholder="이메일"
onChange={onChange}
value={email}
/>
<button onClick={onCreate}>등록</button>
</div>
);
};
export default CreactUser;
let
을 사용해서 변수를 선언하지 않고 굳이 useRef
를 사용하는 점이 단번에 이해가 가지 않지만 useRef
를 사용하면 렌더링될 때마다 해당 값이 초기화되는 것을 피할 수 있다고 한다. 다시말해 렌더링할 요소들을 최대한 줄임으로써 성능적으로 이점을 가질 수 있다는 것이다.