
✔️ useState 사용
export default function Player() {
const [enteredPlayerName, setEnteredPlayerName] = useState("");
const [submitted, setSubmitted] = useState(false);
const handleChange = (e) => {
setSubmitted(false);
setEnteredPlayerName(e.target.value);
};
const handleSubmit = () => {
setSubmitted((submitted) => !submitted);
};
return (
<section id="player">
<h2>Welcome {submitted ? enteredPlayerName : "unknown entity"}</h2>
<p>
<input type="text" onChange={handleChange} value={enteredPlayerName} />
<button onClick={handleSubmit}>Set Name</button>
</p>
</section>
);
}
input에 값을 입력하면 setEnteredPlayerName으로 상태를 업데이트한다.handleSubmit 호출 시 submitted 상태를 true로 변경한다.enteredPlayerName과 submitted 값에 따라 화면이 즉시 업데이트된다.✔️ useRef 사용
import { useState, useRef } from "react";
export default function Player() {
const playerName = useRef;
const [enteredPlayerName, setEnteredPlayerName] = useState(null);
const handleSubmit = () => {
setEnteredPlayerName(playerName.current.value);
};
return (
<section id="player">
<h2>Welcome {enteredPlayerName ?? "unknown entity"}</h2>
<p>
<input ref={playerName} type="text" />
<button onClick={handleSubmit}>Set Name</button>
</p>
</section>
);
}
ref를 input 요소에 연결하여 입력 값을 직접 참조한다.handleSubmit 호출 시 playerName.current.value로 값을 가져온다.setEnteredPlayerName으로 상태를 업데이트하지만, 입력값 변경만으로는 리렌더링이 발생하지 않는다.ref 로 값을 관리하기 때문에 입력값이 즉시 화면에 반영되지 않는다.useState
화면에 즉각적으로 반영되어야 하는 데이터(상태)를 관리할 때 사용한다.
ex) 사용자 입력, 버튼 클릭 등으로 상태가 변화하며 UI를 업데이트할 때
useRef
DOM 접근이 필요하거나 리렌더링 없이 값을 추적해야 할 때 사용한다.
ex) 입력 필드 참조, 컴포넌트 내부에서 유지해야 하는 값 관리
💡 요약
useState는 리렌더링을 유발하여 상태와 UI를 동기화한다.
useRef는 리렌더링 없이 값을 유지하거나, DOM 요소를 참조할 때 사용된다.
화면과 상호작용하는 상태는 useState, 단순히 값을 저장하거나 DOM 참조가 필요한 경우는 useRef를 활용하면 된다.
DOM을 직접 조작하는 것이 아니다. DOM을 조작하느 것은 리액트가 알아서 해준다!
setTimeout지정된 시간 후에 한 번 실행될 콜백 함수를 예약한다.
(호출 시 변수에 반환 가능 --> 이 변수를 통해 타이머를 추적/취소할 수 있다.)
const timer = setTimeout(() => {
console.log("This will run after 3 seconds");
}, 3000);
clearTimeout예약된 타이머를 취소한다.
clearTimeout(timer)를 호출하면, 해당 타이머가 실행되지 않는다.
(이미 실행된 타이머는 취소할 수 없다!)
clearTimeout(timer);
사진과 같이 여러개의 타이머를 구현하려면 각 타이머마다 값을 관리해야 한다.
하지만 밑의 변수를 사용하여 timer를 저장하는 방식은 하나의 타이머 데이터만 관리할 수 있으므로 여러개의 타이머를 작동시킬 때 오류가 난다.
이것을 해결하기 위해 useRef를 사용하면 된다.
✔️ 변수 사용
let timer;
function handleStart() {
timer = setTimeout(() => {
setTimerExpired(true);
}, targetTime * 1000);
setTimerStarted(true);
}
function handleStop() {
clearTimeout(timer);
}
✔️ useRef 사용
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);
}
time.current에 저장된 값은 컴포넌트가 리렌더링 되어도 초기화되지 않는다..let vs useRef| 특징 | let 사용 | useRef 사용 |
|---|---|---|
| 값의 유지 여부 | 리렌더링 시 초기화됨 | 리렌더링 간에도 값 유지 |
| UI 리렌더링 영향 | 로컬 변수는 리렌더링을 발생시키지 않음 | useRef도 리렌더링을 발생시키지 않음 |
| 타이머 ID 관리 안정성 | 리렌더링 후 이전 타이머 ID 접근 불가 | 리렌더링 후에도 타이머 ID 접근 가능 |
| 사용 목적 | 단순히 리렌더링 없는 경우에만 적합 | 타이머, DOM 참조, 상태와 무관한 값 관리에 적합 |
➡️ 타이머처럼 리렌더링 이후에도 값을 유지해야 하는 경우, useRef를 사용하는 것이 가장 안전하다.
.current 란?.current 는 useRef 로 생성된 객체의 속성이다.
useRef를 호출하면 { current: ... } 형태의 객체를 반환하며, .current는 해당 객체의 실제 값을 담고 있다.
const myRef = useRef(initialValue);
useRef는 { current: initialValue } 형태의 객체를 반환한다..current 속성을 통해 값에 접근하거나 변경할 수 있다..current의 역할useRef는 값 변경 시 리렌더링을 발생시키지 않으므로, 리렌더링과 독립적인 데이터 저장소로 사용된다..current를 통해 접근한다..current를 사용해야 할까?리액트에서useRef 로 생성한 참조(ref)는 단순한 변수와 다르다.
useRef는 리액트가 관리하는 특수한 객체를 반환하며, .current 속성을 통해 해당 값을 저장/조회할 수 있다.
💡 결론 :
.current를 쓰는 이유
값에 접근하거나 변경하기 위해
useRef로 반환된 객체는 항상 { current: 값 } 구조를 가지므로, .current를 통해 값을 읽거나 써야 한다.
리렌더링과 독립적인 값 관리
useState는 값 변경 시 리렌더링이 발생하지만, useRef의 .current는 리렌더링 없이 값을 유지하고 관리한다.
부모 컴포넌트가 자식 컴포넌트의 DOM 요소 또는 다른 참조(ref)를 직접 조작할 수 있도록 ref를 전달 (forward) 해주는 고차 함수이다.
기본적으로 리액트의 컴포넌트는 자신이 관리하는 DOM요소에만 접근할 수 있다.
부모 컴포넌트에서 자식 컴포넌트 내부의 특정 DOM 요소를 제어하려면 ref를 전달할 필요가 있다.
✔️ 부모 컴포넌트
const modalRef = useRef();
const openModal = () => {
modalRef.current.showModal(); // 모달 열기
};
return (
<div>
<button onClick={openModal}>Open Modal</button>
<ResultModal ref={modalRef} result="Win" targetTime={10} />
</div>
);
✔️ 자식 컴포넌트
const ResultModal = forwardRef(function ResultModal(
{ result, targetTime }, // Props
ref // 부모로부터 전달받은 ref
) {
return (
<dialog ref={ref} className="result-modal" open> </dialog>
);
forwardRef는 부모가 자식 컴포넌트의 DOM 요소에 직접 접근하도록 해준다.forwardRef를 사용하면 부모가 자식의 내부 DOM 구조를 몰라도 간단히 조작할 수 있다.➡️ 이 구조는 네이티브 DOM 조작이 필요하거나, 특정 상황에서 외부에서 DOM 요소를 제어해야 할 때 유용하다.
리액트 훅 중 하나로, 자식 컴포넌트가 부모 컴포넌트에 전달되는 ref 동작을 커스터마이징하거나 특정 메서드를 노출할 때 사용된다. 주로 forwardRef와 함께 사용한다.
ref 사용 방식ref는 보통 리액트 컴포넌트나 DOM 요소에 직접 접근하기 위해 사용된다.
부모 컴포넌트가 자식 컴포넌트의 DOM 노드에 접근하려면 useRef를 통해 연결된 ref를 이용할 수 있다.
위 방식은 자식 컴포넌트의 내부 DOM 구조에 의존하므로 캡슐화가 부족하다.
useImperativeHandle은 부모가 자식의 내부 구조를 몰라도 제공된 메서드만 사용할 수 있게 한다.
useImperativeHandle(ref, createHandle, [dependencies]);
ref - forwardRef 를 통해 부모로부터 전달된 refreateHandle 객체를 반환하는 함수. 이 객체는 부모가ref.current를 통해 사용할 수 있는 메서드나 값을 정의[dependencies] - 선택적 / createHandle 함수가 재실행될 조건을 지정하는 의존성 배열react vs react-dom| 항목 | React | ReactDOM |
|---|---|---|
| 역할 | React 컴포넌트를 작성하고 상태 관리 | React 컴포넌트를 실제 DOM에 렌더링 |
| 제공 기능 | 컴포넌트 및 상태 관리, Hooks 제공 등 | DOM에 React 앱을 렌더링Portal, SSR 지원 |
| 초점 | UI 설계와 컴포넌트의 동작 | React 컴포넌트와 브라우저 DOM의 연결 |
| 사용 예 | JSX, Hooks, Context 등을 처리 | ReactDOM.createRoot 또는 render 호출 |
| 서버 사이드 렌더링 | 관련 없음 | ReactDOMServer를 통해 구현 |
React - 애플리케이션의 UI와 그 동작을 정의한다.ReactDOM - React가 정의한 UI를 실제 브라우저 DOM에 렌더링한다.React를 다양한 환경에서 사용할 수 있도록 하기 위해서이다.
예를 들어, React Native는 ReactDOM 대신 모바일 앱의 네이티브 환경에 맞는 렌더러를 사용한다.
ReactDOM은 브라우저 환경에서 동작하도록 설계된 렌더러이다.
리액트의 portal 을 생성하는 메서드이다.
기본적으로 리액트의 컴포넌트는 부모 DOM 노드에 렌더링되지만, portal을 사용하면 부모 컴포넌트 계층 구조를 벗어나서 다른 DOM 노드에 리액트 컴포넌트를 렌더링할 수 있다.
createPortal(child, container)
child - 렌더링할 리액트 노드 (컴포넌트, JSX 등)container - 렌더링할 DOM 엘리먼트 (일반적으로 document.getElemenyById('id')로 가져옴)DOM 구조와 스타일 분리
모달, 툴팁, 드롭다운과 같은 컴포넌트를 만들때, 렌더링 위치를 루트 노드로 이동하여 스타일과 동작을 제대로 처리할 수 있다.
z-index 문제 해결
모달이나 팝업이 부모 컴포넌트 내부에 렌더링되면 부모의 overflow: hidden 이나 z-index 등의 스타일이 영향을 미칠 수 있다.
이를 방지 하기 위해 모달을 DOM 계층 밖에 렌더링 해야된다.
💡 주요 포인트
createPortal은 DOM 계층 구조를 무시하고 리액트 컴포넌트를 다