리액트.. 늘 새로워... 짜릿해...
1. useRef란?
일단 useRef는 앞에 use가 붙어있는걸 보아하니.... 왠지 React hool이 아닌가... 했지만
정확히 맞았다. React hook이 맞다. 그렇다면 또 설명을 참을 수 없기 때문에 개념을 먼저 잡고 가보자.
useState()나 useEffect()처럼 많이 쓰이지는 않지만 특정한 DOM를 잡아서 그 DOM을 변경할 수 있는 장점이 있다. 리액트를 사용할 때 간혹 DOM을 직접 선택해서 엘리먼트에게 특정 스타일을 주거나, 스크롤바 위치를 가져오거나, 페이지를 렌더링 했을 때 input에 focus가 돼야 한다거나 그럴 때useRef라는 hooks함수를 사용한다.
2. useRef의 예시
나만 그러는지 모르겠지만 예시를 사용함으로써 보는 사람과 블로그를 작성하는 사람 모두 이해가 좀 더 빨리 된다고 생각해서 예시를 많이 사용하는 편이다.
import React, { useState, useRef } from 'react';
function InputFocus() {
const [inputs, setInputs] = useState({
name: '',
nickname: ''
});
const nameInput = useRef();
const { name, nickname } = inputs;
const onChange = e => {
const { value, name } = e.target;
setInputs({
...inputs,
[name]: value
});
};
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 InputFocus;
코드만 보면 되게 간단해 보일 수 있지만 useRef를 사용하기 전에 공식문서를 보게 되면 current를 사용하는 걸 볼 수 있다.
2-1. current란?
current가 쓰이는 방식은. current로 사용이 된다. 이게 무슨 말인가? 먼저 useRef를 사용할 때 최상단에 const nameInput = useRef();로 useRef를 먼저 설정해 준다.
왜 이렇게 작성했을까?
먼저 DOM에 접근하기 위해서 useRef를 많이 사용하는데 useRef() 자체를 DOM에 넣어서 작업하는 건 불가능하고 변수로 지정해 주고 사용해줘야 한다. nameInput을 변수로 지정해 줬기 때문에 특정 스타일을 줘야 하는 DOM에 Attribute로 ref={}라는 속성을 주면 된다.
위에 예시 코드에선 nameInput.current.focus();를 줌으로써 화면이 렌더링 될 때 input에 focus가 될 수 있도록 만들었다. (제법 유용하다.)
input에 하나만 준 이유는 어차피 키보드를 이용해서 input 두 개에 접근하는 건 할 수 없다. 첫 번째 input에 접근하게 되면 다음 input으로 가는 건 사용자의 선택이기 때문에 처음 포인트만 주어도 된다는 생각이다.나름의 센스
내가 직접 사용한 useRef 예시
import React, { useEffect, useState, useRef } from 'react';
const Best = () => {
const bestRef = useRef();
const [bestCount, setBestCount] = useState(1);
const [bestList, setBestList] = useState([]);
const [buttonActive, setButtonActive] = useState(false);
const handleBestDecrease = () => {
setBestCount(bestCount - 1);
bestRef.current.style.transform = `translateX(-${1200 * bestCount}px)`;
if (0 === bestCount) {
setBestCount(1);
setButtonActive(false);
} else {
setButtonActive(true);
}
};
const handleBestIncrease = () => {
setBestCount(bestCount + 1);
bestRef.current.style.transform = `translateX(-${1200 * bestCount}px)`;
if (bestList.length % 4 <= bestCount) {
setBestCount(bestCount - 1);
setButtonActive(true);
} else {
setButtonActive(false);
}
};
return (
<>
<ul ref={bestRef} className="itemList clear">
{bestList.map(best => {
return (
<li key={best.id}>
<div className="imgWrap">
<img src={best.src} alt={best.title} />
</div>
<dl className="textWrap">
<dt>{best.title}</dt>
<dd>{best.price}</dd>
</dl>
</li>
);
})}
</ul>
<div className="buttonWrap">
<button
type="button"
onClick={handleBestDecrease}
disabled={!buttonActive}
className="btnPrev"
>
이전
</button>
<button
type="button"
onClick={handleBestIncrease}
disabled={buttonActive}
className="btnNext"
>
다음
</button>
</div>
</>
);
};
export default Best;
코드에 대해서 간략하진 않지만 설명해 보면 slide 오픈 소스 API를 사용하지 않고 버튼을 눌렀을 때 슬라이드가 4개씩 넘어가게 만들어야 했다.
그래서 사용한 hook은 useEffect, useState, useRef 이렇게 사용했다.
(+ 이 포스팅은 useRef를 다루는 곳이기 때문에 다른 설명들이 생략이 되는 게 있을 수도 있다.)
우선 증가하는 count를 useState로 클릭 시 데이터의 개수 %(나누기) 4를 해서 올라가는 증감식이 갯수 나누기 4를 한 것과 같거나 크면 다시 카운트 값을 조정하고 버튼을 disabled 속성을 true 값으로 바꿔줬다.
여기서 useRef를 사용한 곳은 <li>를 잡고 있는 <ul>태그에 줬다. 그 이유는 클릭이 될 때마다 translateX값을 유동적으로 움직여야 했기 때문이다.
next 버튼을 클릭하면 -> count + 1 -> count * ul의 너비 -> 한 리스트 화면씩 이동
이라는 아주 좋은 아웃풋이 나오게 된다.
useRef를 사용하면서 자연스럽게 useState도 같이 연계해서 사용해 본 점이 정말 좋았고 스스로도 발전했다고 생각한다. 점점 hook을 사용할수록 다른 곳에 그 값을 참조해서 사용해 볼 수 있지 않을까? 하면서 확장성도 생각하게 되는 인사이트도 있었다. 끝!
'React' 카테고리의 다른 글
[React] 스크롤에 따라 navagation fixed (0) | 2023.01.17 |
---|---|
[React] slide API 없이 슬라이드 구현하기 (0) | 2023.01.17 |
[React] 리스트 검색 기능 / 컴포넌트 재사용 (0) | 2023.01.17 |
[React] useState와 객체 데이터를 사용해서 map 함수 작성 (0) | 2023.01.17 |
[React] Side Effect / useEffect / Clean up Effect (0) | 2023.01.17 |
댓글