[React] useRef() 특정태그 선택하기, 변수를 관리하기

MINEW·2022년 7월 8일
0

1. 기본 특징

  • 변경을 관리합니다. 하지만 리렌더링을 발생 시키지는 않습니다. (useState처럼 값이 변화할때마다 리렌더링 되지 X)
  • 값은 바뀌지만, 리렌더링이 안되서 반영이 안되어 보이는 것입니다. (.current가 변경되었다고 리렌더링 되는 것 X)
  • useRef 용도는 2가지 입니다. (특정태그 선택하기, 변수를 관리하기)
  • useRef의 값을 조회, 수정 할 때는 .current를 이용해야 합니다.
  • useRef는 props.ref로 사용하지 X. 대신에 forwardRef를 사용하면 됩니다.

2. useRef 용도 1: 특정태그 선택하기

1) 기본 예시

// <!-- Bpage.jsx --> 컴포넌트
import { useRef } from 'react'; // 1번) 필수

function Bpage() {

  const nameInput = useRef(); // 2번) 변수에 useRef()를 할당하고
  // 참고) useRef()는 1번만 사용할 수 있는게 아니다. 다른 변수명에 넣으면 여러 번 똑같이 사용가능하다.

  useEffect(() => { // 4번) 다음과 같이 사용할 수 있다
    console.log(nameInput.current); // <input type="text" class="name1" />

    console.log(nameInput.current.className); // name1
    
    nameInput.current.classList.add('name2') // 해당 태그에 className을 추가할 수도 있다

    // nameInput.current.focus(); // load되자마자 input태그를 focus상태가 되게 한다.
  }, [])


  return ( // 3번) ref에 useRef()를 할당한, 변수명을 넣어주면
    <div>
      <input type="text" ref={ nameInput } className="name1" />
    </div>
  )
}

export default Bpage

2) 심화 예시 (forwardRef)

// <!-- 부모 컴포넌트 -->
import React, { useRef } from 'react' // 1번) useRef
import Cat from "./Cat"

// 자식 컴포넌트 안에 있는 이미지 사이즈를 알고싶다면?
export default function CatParent() {
  const catRef = useRef(); // 2번)

  return (
    <div>
      <h4> 고양이가 세상을 구한다 </h4>
      <div>
        // 3번) 이때 ref는 자식 컴포넌트에서 props.ref이런식으로 사용하지 못한다.
        <Cat ref={ catRef } />
        // 7번) catRef로 사용하면 -> 232 이런식으로 값이 나온다 (단, 클릭했을때 이벤트가 발생하는 것이지, 화면이 리렌더링 되는 것은 아니다)
        <button onClick={() => console.log(catRef.current.height)} >고양이의 크기를 알고싶어</button>
      </div>
    </div>
  );
}

// <!-- 자식 컴포넌트 -->
import React, { forwardRef, useEffect } from "react"; // 4번) forwardRef

const Cat = forwardRef((props, ref) => { // 5번) forwardRef()로 함수전체를 감싸준다. 이때, 1번째 인자로 props를 꼭 넣어줘야한다.
  useEffect(() => {
    console.log("리렌더링");
    console.log(ref); // {current: img}
  }, []);

  return (
    <div>
      <img
        src="https://static01.nyt.com/images/2016/03/30/universal/ko/well_cat-korean/well_cat-superJumbo-v2.jpg?quality=90&auto=webp"
        alt="cat"
        style={{ width: "150px" }}
        ref={ ref } // 6번) ref를 넣어주고
      ></img>
    </div>
  );
});

export default Cat;

3. useRef 용도 2: 변수를 관리하기

const mounted = useRef(false) // 1번) 변수에 할당해서

useEffect(() => {
  if (!mounted.current) {
    mounted.current = true // 2번) 값을 사용, 수정 할 수는 있지만 -> 리렌더링은 X
  } else {
    // 함수 내용 작성
  }
}, [변수명])

4. 콜백 ref (화면을 리렌더링 하고 싶을때)

// <!-- 부모 컴포넌트 -->
import React, { useState } from 'react'; // useRef를 사용하지 않는다!
import Cat from "./Cat";

// 자식 컴포넌트 안에 있는 이미지 사이즈를 알고싶다면?
export default function CatParent() {
  const [ height, setHeight ] = useState(0); // 1번) useState 생성하고

  const catCallbackRef = (node) => { // 2번) 콜백함수 생성
    console.log(node); // 6번) 자식 컴포넌트에 <img>태그
    if (node !== null) { // 7번) getBoundingClientRect()는 넓이, 높이 등을 알 수 있는 내장함수
      setHeight(node.getBoundingClientRect().height); // 8번) setState로 값을 업데이트하면 -> 리렌더링 발생한다
    }
  }

  return (
    <div>
      <h4> 고양이가 세상을 구한다 </h4>
      <h3>이미지 높이: { height }</h3> // 9번) 즉, 이 값이 업데이트되면서 -> 페이지 리렌더링
      <div>
        <Cat ref={ catCallbackRef } /> // 3번) useRef 대신에, 함수를 넣어주고
      </div>
    </div>
  );
}

// <!-- 자식 컴포넌트 -->
import React, { forwardRef, useEffect, useState } from "react";

const Cat = forwardRef((props, ref) => {
  useEffect(() => {
    console.log("리렌더링");
    console.log(ref); // 결과가 콜백함수 내용
  }, []);

  const [ loaded, setLoaded ]  = useState(false);

  return (
    <div>
      <img
        src="https://static01.nyt.com/images/2016/03/30/universal/ko/well_cat-korean/well_cat-superJumbo-v2.jpg?quality=90&auto=webp"
        alt="cat"
        style={{ width: "150px" }}
        ref={loaded ? ref: () => undefined} // 5번) 로드되면 -> ref에 ref 들어가고
        onLoad={() => setLoaded(true)} // 4번) 이미지가 다 load된 후, 정확한 값을 가져오려고 사용
      ></img>
    </div>
  );
});

export default Cat;

5. 화면에 반영 (+ useEffect)

const stateRef = useRef(0); // 변수를 관리하기
const tagRef = useRef(); // 특정태그 선택하기

useEffect(() => {
  console.log('------ stateRef ------'); // 6번) 콜백함수는 처음 mount 될때 1번만 실행된다.
  console.log(stateRef.current);
}, [stateRef.current]); // 5번) useEffect 의 배열에 ref 혹은 ref.current 를 넣을 경우, 변경을 인식하지 못하기 때문에

useEffect(() => {
  console.log('------ tagRef ------'); // 8번) 콜백함수는 처음 mount 될때 1번만 실행된다.
  console.log(tagRef.current);
}, [tagRef.current]); // 7번) 특정태그 선택하기 경우에도 useEffect 는 변경을 인식하지 못한다. 화면상에 innerText 가 변경되더라도, useEffect 의 배열에 ref 혹은 ref.current 를 넣을 경우, 변경을 인식하지 못하기 때문에

const changeStateRef = () => {
  stateRef.current = stateRef.current + 1;
};
const changeTagRef = () => {
  tagRef.current.innerText = '싫어요';
};

return (
  <div>
    <button onClick={changeStateRef}>변수 변경하기</button> // 1번) 아무리 클릭해도
    <div>{stateRef.current}</div> // 2번) 화면에는 반영되지 않는다 (리렌더링이 발생하기 전까지는 계속 0 이다)

    <button onClick={changeTagRef}>태그 innerText 변경하기</button> // 3번) 클릭하면
    <div ref={tagRef}>좋아요</div> // 4번) 좋아요 -> 싫어요 로 변경된다. (특정태그 선택하기 용도로 사용했을 경우에는 DOM 을 변경하는 것이기 때문에)
  </div>
)

profile
JS, TS, React, Vue, Node.js, Express, SQL 공부한 내용을 기록하는 장소입니다

0개의 댓글