본문 바로가기
React

[React] useRef (+ useRef로 버튼 슬라이드 만들기)

by 홍두두현 2023. 1. 17.
반응형
리액트.. 늘 새로워... 짜릿해...

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을 사용할수록 다른 곳에 그 값을 참조해서 사용해 볼 수 있지 않을까? 하면서 확장성도 생각하게 되는 인사이트도 있었다. 끝!

반응형

댓글