728x90
반응형
목차
useRef를 통해 코드 간소화하기
아래처럼 input에 값을 넣고 변경하는 것을 만들려면 불필요하게 상태와 코드가 많이 필요하다.
import { useState } from "react";
export default function Player() {
const [Name, setName] = useState("");
const [edit, setEdit] = useState(true);
function handleChangeNmae(e) {
setName(e.target.value);
}
function handleSubmit() {
setEdit(false);
}
return (
<section id="player">
<h2> {edit ? "Welcome unknown entity" : `Welcome ${Name}`}</h2>
<p>
<input type="text" onChange={handleChangeNmae} value={Name} />
<button onClick={handleSubmit}>Set Name</button>
</p>
</section>
);
}
ref는 html요소 접근을 도와주는 참조로 current를 통해 모든 html속성에 접근 가능하게 해준다.
아래와 같이 훨씬 더 간결하게 만들어 줄 수 있다.
import { useState, useRef } from "react";
export default function Player() {
const [Name, setName] = useState("");
const playerName = useRef();
function handleSubmit() {
setName(playerName.current.value);
}
return (
<section id="player">
<h2> {Name ?? "Welcome unknown entity"}</h2>
<p>
<input
ref={playerName}
type="text"
/>
<button onClick={handleSubmit}>Set Name</button>
</p>
</section>
);
}
useRef vs useState
아래 코드는 실행이 될까? 정답은 x이다. 값의 변경이 이루어지지 않는다.
그 이유는 state는 컴포넌트들의 재실행을 야기하지만 참조는 야기하지 않는다.
그래서 useRef는 보통 dom에 직접 접근할때 이용한다.
import { useState, useRef } from "react";
export default function Player() {
//const [Name, setName] = useState("");
const playerName = useRef();
function handleSubmit() {
//setName(playerName.current.value);
//dom상호작용은 리액트가 해야된다는 규칙 위반
playerName.current.value = "";
}
return (
<section id="player">
<h2>
{playerName.current
? "Welcome unknown entity"
: playerName.current.value}
</h2>
<p>
<input ref={playerName} type="text" />
<button onClick={handleSubmit}>Set Name</button>
</p>
</section>
);
}
다음 코드는 timer가 올바르게 작동하지 않는다. 무엇이 문제일까?
바로 timer를 참조하지 않고 전역변수로 쓴 것이다 저렇게 되면 timer는 머추지 않고 다른 중지 버튼에 덮어 씌어지게 된다.
import { useState } from "react";
function TimerChallenge({ title, targetTime }) {
const [timerStarted, setTimerStarted] = useState(false);
const [expired, setExpired] = useState(false);
let timer;
function handleStop() {
clearTimeout(timer);
}
function handleStart() {
timer = setTimeout(() => {
setExpired(true);
}, targetTime * 1000);
setTimerStarted(true);
}
return (
<section className="challenge">
<h2>{title}</h2>
{expired && <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;
따라서 useRef를 통해 모든 컴포넌트 인스턴트들은 독립적인 timer를 갖게 된다.
import { useRef, useState } from "react";
function TimerChallenge({ title, targetTime }) {
const [timerStarted, setTimerStarted] = useState(false);
const [expired, setExpired] = useState(false);
//let timer;
const timer = useRef();
function handleStop() {
clearTimeout(timer.current);
}
function handleStart() {
timer.current = setTimeout(() => {
setExpired(true);
}, targetTime * 1000);
setTimerStarted(true);
}
return (
<section className="challenge">
<h2>{title}</h2>
{expired && <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를 다른 컴포넌트에서 쓰기위한 방법중 하나인 함수이다.
import { forwardRef, useImperativeHandle, useRef } from "react";
//ref는 따로 뺴준다.
const ResultModal = forwardRef(function ResultModal(
{ result, targetTime },
ref
) {
return (
<dialog ref={ref} className="result-modal">
<h2>You {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;
//이런식으로 다른 컴포넌트에서 ref를 활용할 수 있게된다.
const dialog = useRef();
<ResultModal
ref={dialog}
result={"lose"}
targetTime={targetTime}
></ResultModal>
useImperativeHandle
부모 컴포넌트가 자식 컴포넌트의 내부 인스턴스를 직접 제어할 수 있게 해준다. 이를 통해 자식 컴포넌트의 특정 기능을 외부에서 사용할 수 있도록 노출할 수 있다.
import { forwardRef, useImperativeHandle, useRef } from "react";
const ResultModal = forwardRef(function ResultModal(
{ result, targetTime },
ref
) {
const dialog = useRef();
//컴포넌트 함수에서호출하여속성과메소드를 정의할 수 있다.-> 재사용이 가능하게 만들어준다.
useImperativeHandle(ref, () => {
return {
open() {
dialog.current.showModal();
},
};
});
return (
<dialog ref={dialog} className="result-modal">
<h2>You {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;
createPortal
내가 만든 모달의 위치를 확인해보면 body의 하위요소와 가깝다.
나는 html의 상위 요소에 위치시키고 싶다 이럴 때 createPortal을 써주면 된다.
이런식으로 html의 상위 요소에 들어간 것을 볼 수 있다.
사용방법은 아래와 같다.
modal component
import { forwardRef, useImperativeHandle, useRef } from "react";
import {createPortal} from "react-dom";
const ResultModal = forwardRef(function ResultModal(
{ targetTime, remainingTime, onReset },
ref
) {
const dialog = useRef();
const userLost = remainingTime <= 0;
const score = Math.round((1 - remainingTime / (targetTime * 1000)) * 100);
const formmatRemainingTime = (remainingTime / 1000).toFixed(2);
useImperativeHandle(ref, () => {
return {
open() {
dialog.current.showModal();
},
};
});
//createPortal써주고 getElementById로 modal요소 접근
return createPortal(
<dialog ref={dialog} className="result-modal">
{!userLost && <h2>Score: {score}</h2>}
{userLost && <h2>You Lost</h2>}
<p>
The target time was <strong>{targetTime} seconds.</strong>
</p>
<p>
You stopped the timer with{" "}
<strong>{formmatRemainingTime} seconds left.</strong>
</p>
<form method="dialog" onSubmit={onReset}>
<button>Close</button>
</form>
</dialog>,
document.getElementById('modal')
);
});
export default ResultModal;
index.html
<body>
<div id="modal"></div>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
반응형
'코딩 정보 > React' 카테고리의 다른 글
[React] useReduce의 간략한 사용방법을 알아보자 (0) | 2024.07.14 |
---|---|
[React] context api를 이용해서 props drilling 막기 (0) | 2024.07.14 |
[React] 디버깅의 방법을 예를 통해 알아보자 (0) | 2024.06.30 |
[React] css에 대해 알아보자 [styled component] (0) | 2024.06.30 |
[React] css에 대해 알아보자 [바닐라 css] (0) | 2024.06.29 |