useRef 어떻게 사용하고 있나?

하도야지·2021년 9월 15일
0

Ref는 무엇인가?

  • 공식문서

useRef는 .current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환합니다.


왜 Ref로 써야할까?

  1. Dom 접근 방식
  • Vanila Javascript - querySelector / getElementById
  • React에서 데이터는 State로 조작되기에 DOM API와 혼합해서 데이터 및 조작할 경우 디버깅 및 유지보수 어려움
  1. ID 중복
  • map 메소드를 이용해 랜더링 되는 리스트형태 Element는 같은 ID 사용
  • ref는 전역적으로 작동하지 않고 컴포넌트 내부에서만 작동
  1. 렌더링 시점과 element 존재 여부
  • Vitual Dom의 react에선 렌더링이 끝나기 전에는 페이지에 해당 element가 존재하지 않음
  • 초기 렌더링 중에는 아직 컴포넌트에 생성된 구조가 없음

언제 Ref를 써야할까?

  1. 특정 Dom을 선택할 때
    ex) 초기화 시 focus 처리

  2. 컴포넌트 안에서 조회 및 수정 가능한 변수를 생성할 때(랜더링되지 않으면서)
    ex) onChange 없이 input 관리하기


적용 예시

  • State / Ref 변경 시 리렌더링 확인하기
import React, { useRef, useState } from 'react';
import siroo from './siroo.jpg'

const RefSample1 = () => {
    const a = 1;
    const [state, setState] = useState(1);
    const ref = useRef(1);
  
    return (
      <div>
        <h1>1. 변경은 관리O! 리렌더링을 발생X!</h1> 

        <ul>
          <li>일반 변수: {a}</li>
          <li>state 변수: {state}</li>
          <li>ref 변수: {ref.current}</li>
        </ul>

        <img src={siroo} width="300" height="300" alt=""/> 
        <br/>
        
        <button onClick={() => setState(state + 1)}>hello</button>
        <button onClick={() => (ref.current = ref.current + 1)}>
          집사야 눌러봐
        </button>
      </div>
    );
};

export default RefSample1;
  • Ref로 input값 관리하기
import React, { useRef, useState } from "react";

const SignInForm = (props) => {

  const { signInList, setSignInList, articleNumber } = props;
  
  // *************************1. Ref 객체 만들기 **********************
  const inputName = useRef(null);
  const inputAge = useRef(null);
  const inputEmail = useRef(null);
  const inputPassword = useRef(null);

  //  ************************ 3.current로 돔 접근하기 *****************

  const signUpByRef = () => {

    const newSignInForm = {
      articleNumber : articleNumber.current,
      name: inputName.current.value,
      age: inputAge.current.value,
      email: inputEmail.current.value,
      password: inputPassword.current.value,
    };
    
    setSignInList([...signInList, newSignInForm]);

    alert("회원가입되셨습니다.");
    
    inputName.current.value = "";
    inputAge.current.value = "";
    inputEmail.current.value = "";
    inputPassword.current.value = "";

    articleNumber.current += 1;
  };
  
  const reset = () => {
    inputName.current.value = "";
    inputAge.current.value = "";
    inputEmail.current.value = "";
    inputPassword.current.value = "";
    inputName.current.focus();
  };


  return (
    <div>
      <h1>회원가입 폼 By REF</h1>
      <div>
        <ul>
          <li>
            <label>아이디</label>
            <input
              name="name"
              type="text"
              // ********************2. 선택할 DOM에 ref 값 설정****************************
              ref={inputName}
              //***************************************************************************
            ></input>
          </li>
          <li>
            <label>나이</label>
            <input
              name="age"
              type="text"
              ref={inputAge}
            ></input>
          </li>
          <li>
            <label>이메일</label>
            <input
              name="email"
              type="text"
              ref={inputEmail}
            ></input>
          </li>
          <li>
            <label>비밀번호</label>
            <input
              name="password"
              type="password"
              ref={inputPassword}
            ></input>
          </li>
        </ul>
      </div>
      <div>
        <button onClick={reset}> 초기화 </button>
        <button onClick={signUpByRef}> 회원가입 </button>
      </div>
    </div>
  );
};

export default SignInForm;
  • ref로 화면 리사이징하기
import { observer } from 'mobx-react';
import React, { useEffect, useRef, useState } from 'react';
import KitchenOrderFrame from '../../component/kitchen/KitchenOrderFrame';
import UseStore from '../../stores/UseStore';
import EventSource from 'eventsource';
import ArrowIcon from '../../component/kitchen/ArrowIcon';
import EmptyBox from '../../component/kitchen/EmptyBox';
import { NOTI_KITCHEN } from '../../utils/config';
import './index.css';


const Kitchen = observer(() => {

  const { OrderStore } = UseStore();
  const [topNum, setTopNum] = useState(0);
  const kitchenContainer = useRef();

  //메인 useEffect : 초기 목록조회, 사이징처리, sse세팅
  useEffect(() => {
    OrderStore.fetchOrdersInProgress().then();
    resizing();

    const es_ = new EventSource(NOTI_KITCHEN);
    es_.onmessage = (e) => {
      const reqWaitingNum = JSON.parse(e.data).data.waitingNum;
      OrderStore.addOrdersInProgressBySSE(reqWaitingNum);
    };
    return () => {
      es_.close();
    };
  }, [OrderStore]);

  //서브 useEffect : 리사이징 함수 호출
  useEffect(() => {
    window.addEventListener('resize', resizing);
    return () => {
      window.removeEventListener('resize', resizing);
    };
  }, []);

  // window 크기가 변할때마다 노출 갯수 변경
  const resizing = () => {
    setTopNum(() =>
    Math.floor((kitchenContainer.current.clientWidth -140)  / 326)
    );
  };

  return (
    <>
      <div className={'kitchen-container'} ref={kitchenContainer}>
        <div className={'left-arrow-space'}>
          <ArrowIcon direction={'left'} />
        </div>
        {OrderStore.ordersInProgressOnView.length === 0 ? (
          <EmptyBox />
        ) : (
          OrderStore.ordersInProgressOnView
            .slice(undefined, topNum)
            .map((order) => (
              <KitchenOrderFrame key={order.orderId} order={order} />
            ))
        )}
        <div className={'right-arrow-space'}>
          <ArrowIcon direction={'right'} />
        </div>
      </div>
    </>
  );
});

export default Kitchen;
profile
프로그래머를 꿈꾸는 코더

0개의 댓글