Learn useRef

Junghan Lee·2023년 4월 10일
0

Learnd in Camp

목록 보기
26/48

useRef?

함수형 컴포넌트에서 'ref'를 생성하고 관리하는 hook으로 useRef를 사용하면 일반적인 'ref'와 마찬가지로 DOM 노드나 다른 리액트 컴포넌트를 참조할 수 있다.
'useRef'의 핵심 개념은 ref객체를 생성하고 해당 ref객체를 통해 컴포넌트 내에서 DOM노드나 다른 React컴포넌트를 참조하는 것이다. 이러한 ref객체는 함수형 컴포넌트에서도 동일하게 작동하며 useRef를 사용하면 쉽게 생성하고 관리할 수 있다.
useRef에는 두 가지 주요 용도가 있는데 첫쨰, ref객체를 생성하고 둘째, 컴포넌트가 업데이트될 때 이전 ref값을 유지한다. 이러한 기능을 통해 useRef는 컴포넌트에서 DOM요소의 크기, 위치 등을 측정하거나 이전 값과 현재 값을 비교하여 변경 여부를 결정하는 작업 등에 사용될 수 있다.

import { useRef, useState, useEffect } from 'react';

function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();

useEffect(() => {
  prevCountRef.current = count;
}, [count]);

const prevCount = prevCountRef.current;

useEffect(() => {
  if (prevCount !== undefined) {
    console.log(`count changed from ${prevCount} to ${count}`);
  }
}, [count, prevCount]);

return (
  <div>
    <button onClick={() => setCount(count + 1)}>+</button>
    <button onClick={() => setCount(count - 1)}>-</button>
    <p>Count: {count}</p>
  </div>
);
}

위의 코드는 useRef를 사용해 이전 count 값과 현재 count 값을 비교해 count값이 변경될 때마다 console.log를 호출하는 컴포넌트다. 이 예제에서 prevCountRef.current를 사용해 이전 count 값을 가져와 현재 count값과 비교해 변경 여부를 결정하고 이를 통해 console.log를 호출해 변경된 count값을 콘솔에 출력할 수 있다.

또 useRef는 HTML 파일 태그의 사용에서 쓰이는데...

HTML 기본 FILE 태그 숨기기


이 못생긴 기본 input 태그를 숨기고 꾸며주는 방법은 크게 2가지가 있다.
useRef
HTML태그를 선택할 때 보통 getElementId 사용, react에서는 html태그에 접근을 도와주는 역할을 useRef가 하고 있다. 간단한 사용 방법은?

import { useRef } from 'react';

export default function ImageRefPage(): JSX.Element {
	const fileRef = useRef<HTMLInputElement>(null);

}

Import로 useRef를 가져오고 constfileRef = useRef() 작성하는 것이 가장 처음 Ref를 불러오고 사용하는 기본 설정이다. useState나 useEffect처럼 react에서 가져와야 사용할 수 있다. 이렇게 하고 fileRef를 태그에 연결해주면 해당 태그는 fileRef로 불러서 사용할 수 있다.

<input
  style={{ display: "none" }}
  onChange={onChangeFile}
  type="file"
  ref={fileRef}
/> 

이렇게 input태그에 ref={fileRef}를 작성하면 이제 input 태그를 fileRef를 이용해 사용할 수 있다. 그리고 나서, useRef에 있는 기능을 이용하면 된다. 참고로 useRef에는 다양한 기능이 있다.

const onClickImage = (): void => {
    // 기존 방식: document.getElementById("파일태그ID")?.click();
    fileRef.current?.click();
  };

onClick함수의 Ref에는 current안에 click이라는 기능이 있는데 이는 이름 그대로 current는 fileRef에 들어온 태그를 뜻하고 그 태그를 click하겠다는 기능이다.

<button onClick={onClickImage}>이미지 등록 버튼</button>

그리고 새로운 버튼을 하나 만들어 해당 기능을 넣어준다.
이러면 버튼을 클릭했을 때 fileRef.current.click()이 실행될 것이고 그건 useRef에 넣어두었던 input 태그를 클릭한 것과 같은 결과가 나올 것이다.

응용

return (
		<>
			<div>
				<img onClick={onClickImage} style={{ width: '500px' }} id="image" />
				<input
					hidden={true}
					ref={fileRef}
					type="file"
					onChange={readImage}
				></input>
				<button onClick={onClickImage}>이미지 등록 버튼</button>
			</div>
		</>
	);

위 코드에선 미리보기 이미지에도 onClickimage를 넣어주었는데 이 기능이 어떤 기능인지는 위에서 이야기했고 input태그에는 hidden기능을 이용해 태그를 숨겨주었다. 이러면 미리보기 이미지를 클릭할 때, 이미지 등록 버튼을 클릭할 때 모두 input type=file태그를 클릭한 것과 동일한 결과를 볼 수있다.

import { gql, useMutation } from "@apollo/client";
import { ChangeEvent, useRef, useState } from "react";
import {
  IMutation,
  IMutationUploadFileArgs,
} from "../../../src/commons/types/generated/types";

const UPLOAD_FILE = gql`
  mutation uploadFile($file: Upload!) {
    uploadFile(file: $file) {
      url
    }
  }
`;
export default function ImageRefPage(): JSX.Element {
  const [imageUrl, setImageUrl] = useState("");
  const fileRef = useRef<HTMLInputElement>(null);

  const [uploadFile] = useMutation<
    Pick<IMutation, "uploadFile">,
    IMutationUploadFileArgs
  >(UPLOAD_FILE);
  const onChangeFile = async (
    event: ChangeEvent<HTMLInputElement>
  ): Promise<void> => {
    const file = event.target.files?.[0]; // 배열로 들어오는 이유: <input type="file" multiple/>일때, 여러 개 업로드 가능하기 때문입니다.
    const result = await uploadFile({ variables: { file } });
    setImageUrl(result.data?.uploadFile.url ?? "");
  };

  const onClickImage = (): void => {
    // 기존 방식 : document.getElementById("파일태그ID")?.click();
    fileRef.current?.click();
  };
  return (
    <>
      <div
        style={{ width: "50px", height: "50px", backgroundColor: "gray" }}
        onClick={onClickImage}
      >
        이미지 선택
      </div>
      <input
        style={{ display: "none" }}
        onChange={onChangeFile}
        type="file"
        ref={fileRef}
      />
      <img src={`https://storage.googleapis.com/${imageUrl}`} />
    </>
  );
}

위는 전체 코드이다.

(참고)label태그와 htmlFor 이용하기
Label태그에는 htmlFor이라는 속성이 있는데 이속성에 값을 넣으면 갑소가 똑같은 id를 찾아 그 태그의 기능과 연결해준다.

<div>
				<label htmlFor="fileTag">이거 눌러도 실행돼요!</label>
				<img style={{ width: '500px' }} id="image" />
				<input id="fileTag" type="file" onChange={readImage}></input>
</div>

htmlFor='fileTag'와 input id="filetag'의 값이 똑같다.

결과적으로 label태그를 클릭해도 똑같이 파일을 올릴 수 있다. 그러면 label태그를 원하는 디자인으로 꾸미고 기존 input태그는 안보이도록 css작업하면 된다.

profile
Strive for greatness

0개의 댓글