벨로퍼트 React 내용 정리 (1장 10~15)
출처: https://react.vlpt.us/basic/
1. useRef로 특정 DOM 선택하기
- 배경 : 리액트에서도 특정 DOM 을 직접 선택해야 하는 상황이 발생할 수 있다.
ex. 특정 엘리먼트의 크기를 가져오기, 스크롤바 위치 가져오기, 포커스 설정하기, 그래프 관련 라이브러리 등의 외부 라이브러리 사용시 특정 DOM에 적용하기 위해 DOM을 선택할 때
- 예시 : 초기화 버튼을 클릭했을 때 포커스가 초기화 버튼에 그대로 남아있지 않고 이름 input 에 포커스가 잡히도록 하기
- 사용방법 : useRef() Hook 함수를 사용하여 Ref 객체를 만들고, 이 객체를 선택하고 싶은 DOM 에 ref 값으로 설정해주면, Ref 객체의 .current 값은 우리가 원하는 DOM 을 가르키게 된다.
예시에서는 onReset 함수에서 input 에 포커스를 하는 focus() DOM API 를 호출해주었다.
예시 1)
function InputSample() {
const nameInput = useRef();
// const nameInput = useRef<HTMLInputElement | null>(null); // Typescript
const onReset = () => {
setInputs({
name: '',
nickname: ''
});
nameInput.current.focus();
};
return (
<div>
<input
name="name"
placeholder="이름"
onChange={onChange}
value={name}
ref={nameInput}
/>
<input
name="nickname"
placeholder="닉네임"
onChange={onChange}
value={nickname}
/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
{name} ({nickname})
</div>
</div>
);
}
export default InputSample;
예시 2)
const downloadRef = useRef<HTMLImageElement>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
const popper = usePopper(downloadRef.current, popperElement, {
placement: 'bottom-end',
}); // usePopper 훅을 사용하여 팝오버(Popover)나 툴팁(Tooltip)과 같은 UI 요소의 위치를 계산하고 관리
<DownloadPeriod
src={calImg}
ref={downloadRef}
onClick={onClickHandler}
/>
2. 배열 렌더링
map() 으로 동적인 배열을 렌더링 할 때 key를 삽입하는 이유는?
key가 없을 시 : 배열이 변경될 시에 변경 된 순서에 따라 다음 모든 요소가 변경됨
key가 있을 시 : 배열 업데이트 시 수정되지 않는 기존의 값은 그대로 두고 원하는 곳에 내용을 삽입하거나 삭제
3. useRef로 컴포넌트 안의 변수 만들기
useRef Hook 의 DOM 을 선택하는 용도 외에 다른 용도는!
=> 컴포넌트 안에서 조회 및 수정 할 수 있는 변수를 관리하는 것
배경 : useRef 로 관리하는 변수는 값이 바뀐다고 해서 컴포넌트가 리렌더링되지 않고, 설정 후 바로 조회 할 수 있다.
이 변수를 사용하여 다음과 같은 값을 관리 할 수 있다.
- setTimeout, setInterval 을 통해서 만들어진 id
- 외부 라이브러리를 사용하여 생성된 인스턴스
- scroll 위치
예시 : 배열에 추가한 새 항목에서 사용 할 고유 id 를 관리하는 용도로 useRef 를 사용하기
3-1. 배열에 항목 추가하기
불변성을 지키면서 배열에 새 항목을 추가하는 방법 (push, splice, sort X)
1. spread 연산자 사용
2. concat 함수 사용
3-2. 배열에 항목 제거하기
불변성을 지키면서 특정 원소를 배열에서 제거하기 위해서는 특정 조건에 만족하는 원소들만 추출하여 새로운 배열을 만드는 filter 배열 내장 함수를 사용한다.
3-3. 배열에 항목 수정하기
예시 : 계정명을 클릭했을때 색상이 초록색으로 바뀌고, 다시 누르면 검정색으로 바뀌도록 onToggle 함수를 구현
배열의 불변성을 유지하면서 배열을 업데이트 할 때 map 함수를 사용한다.
id 값을 비교해서 id 가 다르다면 그대로 두고, 같다면 active 값을 반전시키도록 구현을 한다.
function App() {
const [users, setUsers] = useState([
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com'
active: true
},
{
id: 2,
username: 'tester',
email: 'tester@example.com'
active: false
},
{
id: 3,
username: 'liz',
email: 'liz@example.com'
active: false
}
]);
const nextId = useRef(4); // useRef() 를 사용하여 nextId 라는 변수 생성
// useRef() 안에 파라미터를 넣어주면, 이 값이 .current 값의 기본값이 된다.
// 이 값을 수정 할때에는 .current 값을 수정하면 되고 조회 할 때에는 .current 를 조회하면 된다.
// 3-1. 배열 추가
const onCreate = () => {
const user = { // 배열에 추가할 새 항목 생성
id: nextId.current,
username,
email
};
// 배열에 새 항목 추가하는 방법 2가지
// 1. spread 연산자 사용
setUsers([...users, user]);
// 2. concat 메서드 사용
setUsers(users.concat(user));
// input 박스 초기화
setInputs({
username: '',
email: ''
});
// 다음 요소를 위해 id 값 변경
nextId.current += 1;
};
// 3-2. 배열 제거
const onRemove = id => {
// user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
// = user.id 가 id 인 것을 제거함
setUsers(users.filter(user => user.id !== id));
};
// 3-3. 배열 수정
const onToggle = id => {
setUsers(
users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
)
);
};
return <UserList users={users} onRemove={onRemove} onToggle={onToggle} />; // 배열을 App 에서 선언하고 UserList 에게 props로 전달
}
function User({ user, onRemove, onToggle }) {
return (
<div>
<b style={{
cursor: 'pointer', // 마우스를 올렸을때 커서가 손가락 모양으로 변경
color: user.active ? 'green' : 'black' // active 값에 따라 폰트의 색상 변경
}}
onClick={() => onToggle(user.id)}
>
{user.username}
</b>
<span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({ users, onRemove, onToggle }) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove} onToggle={onToggle} />
))}
</div>
);
}
export default UserList;