본문 바로가기

클라이언트/React
[리액트(React)] 참조(Refs)

// 참고, Refs

- DOM 요소에 직접 접근할 때나 UI에 직접적인 영향을 주지 않는 값을 제어할 때 사용한다.

- useRef Hook을 import 해서 사용한다. (https://sorrel012.tistory.com/362 의  useRef 참고)

 

[리액트(React)] Hook

// Hook - function 컴포넌트에서 class컴포넌트와 동일한 기능을 사용할 수 있게 해준다. - 이름 앞에 use를 붙여 hook임을 나타낸다. - 최상위 컴포넌트에서만 호출해야 한다. - 렌더링될 때마다 늘 같은

sorrel012.tistory.com


- useRef로부터 받는 참조 값들은 항상 JavaScript 객체이다.

- HTML 요소에 ref를 통해 참조변수를 설정하면 참조변수명.current를 통해 실제 참조 값을 얻을 수 있다.

<html요소 ref={참조변수명}>
const 변수 = 참조변수명.current.html요소에내장된값 (input의 경우 value)

 

* input 에 useState()로 value를 사용해서 값을 받아온 후 다른 곳에 사용하려고 한다면 특정 이벤트 발생 시 최종 결과값만 받아올 수 없고 값을 변경할 때마다 받아오게 된다.
ref 를 사용하면 이 문제를 해결할 수 있다!


- ref가 바뀔 때마다 컴포넌트 함수가 재실행되지 않는다.(<-> state)


 

import { useRef, useState } from 'react';

export default function Player() {
  const playerName = useRef();

  const [enteredPlayerName, setEnteredPlayerName] = useState(null);

  function handleClick() {
    setEnteredPlayerName(playerName.current.value);
  }

  return (
    <section id="player">
      <h2>Welcome {enteredPlayerName ?? 'unknown entity'}</h2>
      <p>
        <input ref={playerName} type="text" />
        <button onClick={handleClick}>Set Name</button>
      </p>
    </section>
  );
}

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

function TimerChallenge({ title, targetTime }) {
  const timer = useRef();

  const [timerStarted, setTimerStarted] = useState(false);
  const [timerExpired, setTimerExpired] = useState(false);

  function handleStart() {
    timer.current = setTimeout(() => {
      setTimerExpired(true);
    }, targetTime * 1000);

    setTimerStarted(true);
  }

  function handleStop() {
    clearTimeout(timer.current);
  }

  return (
    <section className="challenge">
      <h2>{title}</h2>
      {timerExpired && <p>You lost!</p>}
      <p className="challenge-time">
        {targetTime} second{targetTime > 1 ? 's' : ''}
      </p>
      <p>
        <button onClick={timerStarted ? handleStop : handleStart}>
          {timerStarted ? 'Stop' : 'Start'} Challenge
        </button>
      </p>
      <p className={timerStarted ? 'active' : undefined}>
        {timerStarted ? 'Time is running...' : 'Timer inactive'}
      </p>
    </section>
  );
}

export default TimerChallenge;

// 참조전달, forwardRef

- 참조를 다른 컴포넌트로 전달하고 그걸 그 컴포넌트에서 사용할 수 있게 한다.


// 전달하는 컴포넌트

<ref를 사용할 컴포넌트명
  ref={ref명}
/>

// 사용하는 컴포넌트

1. 컴포넌트 함수를 감싸 상수로 저장한다.

const 컴포넌트명 = forwardRef(function 컴포넌트명() {
});


2. 두 번째 매개변수로 ref를 받는다.

const 컴포넌트명 = forwardRef(function 컴포넌트명(props, ref) {
});


3. 사용할 부분에 참조를 선언한다.

return (
  <dialog ref={ref}></dialog>
);

 

import React, { forwardRef } from 'react';

const ResultModal = forwardRef(function ResultModal(
  { result, targetTime },
  ref,
) {
  return (
    <dialog ref={ref} className="result-modal">
      <h2>{result}</h2>
      <p>
        The target time was <strong>{targetTime} seconds.</strong>
      </p>
      <p>
        You stopped the timer with <strong>X seconds left.</strong>
      </p>
      <form method="dialog">
        <button>Close</button>
      </form>
    </dialog>
  );
});

export default ResultModal;

 

function TimerChallenge({ title, targetTime }) {
  const timer = useRef();
  const dialog = useRef();

  const [timerStarted, setTimerStarted] = useState(false);
  const [timerExpired, setTimerExpired] = useState(false);

  function handleStart() {
    timer.current = setTimeout(() => {
      setTimerExpired(true);
      dialog.current.showModal();
    }, targetTime * 1000);

    setTimerStarted(true);
  }
  function handleStop() {
    clearTimeout(timer.current);
  }

  return (
    <p>
      <button onClick={timerStarted ? handleStop : handleStart}>
        {timerStarted ? 'Stop' : 'Start'} Challenge
      </button>
    </p>
  );
}
export default TimerChallenge;