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

hongduhyeon·2022년 4월 3일
0
post-thumbnail
리액트.. 늘 새로워... 짜릿해...

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

0개의 댓글