2025.03.10 - [React] - React(리액트) 티키타카 23 (useDebounce Hook(훅))
글을 그렇게 마무리 지은 게 빨간 볼펜심으로 옆구리 살에 숨은 양심을 찌른다.
이야기를 꺼냈다면 다이어트 체형이 나올 때까정 해야 그렇게 그렇게
간식도 챙겨야 진정한 코스 요리 다이어트 맛이 아니던가?
use-debounce 모듈에는 useDebouncedCallback 이란 것도 들어있다.
꽌씸을 끌어야 하니, 먼저 아래 재미진 결과를 만드는 소스를 복사/붙여넣기 해성,
그 결과를 시험해 보는 시간을 가지장.
(맘에 안드는 고참을 괴롭힐 때 쓰면 참 좋당!~~, 조금만 머리가 좋다면 대박이당.)
App.jsx
import { useState } from "react";
import { useDebouncedCallback } from "use-debounce";
function App() {
const [schText, setSchText] = useState("");
const e7eChg = useDebouncedCallback((nVal) => {
setSchText(nVal);
}, 500);
return (
<>
<h1>입력으로 괴롭히기</h1>
<input
type="text"
autoFocus
value={schText}
onChange={(e) => e7eChg(e.target.value)}
/>
</>
);
}
export default App;
괴롭히기용 결과 화면
실행해 보면 input type=text에 글씨가 제대로 안 써지는 걸 알수 있당.
분명 콤퓨타가 이상해라고 생각할 수 있을 정도당.~~ ㅋㅋ
소스 흐름을 삐딱하게 잠시 보면, input type=text에 글자가 바뀌면
함수 e7eChg를 부르면서 input type=text의 값을 넘긴당.
함수 e7eChg는 useDebouncedCallback 훅에 의해 리턴된 함수로
바로 schText 값을 바꾸지 않고, 0.5초 뒤에 바꾼다는 의미당.
근데 이게 왱? input type=text에 제대로 글자가 쓰여지기 않게 만들깡?
중요한 문제이니, 생각할 시간을 주겠당.
(최장 5분 꼬옥 한번 생각해 보길...., 어마 무시한 복을 받을 확률을 거머쥔당!.)
원인은 input 에 들어있는 value={schText} 때문이당.(오켕?)
<input
type="text" autoFocus
value={schText}
onChange={(e) => e7eChg(e.target.value)}
/>
요렇게 value에 상태변수를 할당한 컴포넌트를 멋진말로 controlled 컴포넌트라
부르는데, schText값이 delay되어 바뀌니, 당연 이상해 질 수 밖에 없다.
아래 처럼 바꾸고 실행해 보장. (입력이 잘 될기당!)
<input
type="text" autoFocus
defaultValue=""
onChange={(e) => e7eChg(e.target.value)}
/>
이렇게 초기값만 주고, 상태변수와 연결시키지 않은 컴포넌트를 멋진 말로
uncontrolled 컴포넌트라 불린당. 그냥 원래 방식대로 동작하게 된당.
어쩌면 개발자 툴(F12) console에서 아래와 같은 메세지를 볼 수도 있당.
A component is changing an controlled input to be uncontrolled.
..... 생략 .... 의미 알았으면 무시당.
이제 setTimeout과 똑같이 생긴 useDebouncedCallback 사용법을 알았으니
적극 적용하여 사용법에 느끼미를 얹어 취득하장.
아래 코드를 복사/붙여넣기 하고, useDebouncedCallback 사용된 부분만
누네 힘주고 본 다음, 결과를 확인하장. 이젠 동작이 이해 안될 이유를 찾을 수가 없을거당.
아 이름 찾는 거에 이전 글에선 includes를 썼는데, 이번엔 startsWidth를 썼당. (차이 알지용?)
App.jsx
import { useEffect, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
// 더미 데이터
const dummyData = [
"경미니",
"카리나",
"경로제",
"경제니",
"경선주",
"장원영",
"초아",
"문가영",
"김남길",
"차은우",
"이민호",
"안효섭",
"오호",
"박서준",
"김다미",
];
function nameSch(schWord) {
/* 0.2초 ~ 1초 사이 발생 */
let ranTime = Math.round(Math.random() * 800) + 200;
return new Promise((resolve) => {
setTimeout(() => {
// includes 안쓰고 startsWith 사용
resolve(dummyData.filter((name) => name.startsWith(schWord)));
}, ranTime);
});
}
function App() {
const [schText, setSchText] = useState("");
const [schNames, setSchNames] = useState([]);
// useDebouncedCallback 을 이용하여 delay
const e7eChg = useDebouncedCallback((nVal) => {
setSchText(nVal);
}, 500);
useEffect(() => {
const getNames = async () => {
const newNames = await nameSch(schText);
setSchNames(newNames);
};
getNames();
}, [schText]);
return (
<>
<h1>껌색</h1>
<input
type="text"
defaultValue={""}
onChange={(e) => e7eChg(e.target.value)}
/>
<ul>
{schNames.length ? (
schNames.map((name, i) => <li key={i}>{name}님</li>)
) : (
<h1>없엉</h1>
)}
</ul>
</>
);
}
export default App;
결과는 아래 처럼 깔끄미당.(서버에 요청 횟수를 줄여 서버부하를 줄인 게 뽀인또!)
사용자가 불편을 느끼지 않을 정도의 delay 시간을 설정해야 한당.
이왕저왕 한 김에 한개 더 해보장. 추가적으로 제공되는 useThrottledCallback도 써보장.
아래 소스는 scroll 이벤트를 사용하는 소스당. style 무시하고 보면 거저 쉽당.
App.jsx
import { useRef, useState } from "react";
const names = [
"경미니",
"선주니",
"미누니",
"지누니",
"자라니",
"그러니",
"머라니",
"잼나니",
"웃기니",
];
function App() {
const renderedCount = useRef(0);
renderedCount.current++;
const [position, setPosition] = useState(window.scrollY);
const updatePosition = (e) => {
console.log(e.target.scrollTop);
setPosition(e.target.scrollTop);
};
return (
<>
<h1 style={{ textAlign: "center" }}>스크롤 이벤트 딜레이</h1>
<div
style={{
border: "3px solid pink",
color: "white",
textAlign: "center",
backgroundColor: "saddlebrown",
}}
>
<p>
딜레이 top위치{" "}
<span style={{ color: "purple", fontSize: "2em" }}>
{Number.parseInt(position)}
</span>
</p>
<p>
Rendering 몇번{" "}
<span style={{ color: "blue", fontSize: "2em" }}>
{renderedCount.current}
</span>
</p>
</div>
<div
style={{
height: 150,
border: "10px groove gold",
position: "relative",
overflowY: "scroll",
overflowX: "hidden",
}}
onScroll={updatePosition}
>
{names.map((name) => (
<h1 key={name} style={{ textAlign: "center" }}>
{name}
</h1>
))}
</div>
</>
);
}
export default App;
실행도 잘 될 꺼이당. 렌더링 카운트에 주목하장 빛의 속도로 다시 그려댄당.
이제 위 소스를 아주 쪼메만 고쳐보장 (추가 1줄, 수정 1곳이당)
/* 추가 : updataPosition을 0.5초 딜레이 시켜주는 함수 만들기 */
const scrollPosition = useThrottledCallback(updatePosition, 500);
/* 수정: 스크롤 이벤트에서 딜레이된 함수 호출 */
onScroll={scrollPosition} // 수정
노파심에 수정된 소스 전체를 붙여버린당.~~
App.jsx
import { useRef, useState } from "react";
import { useThrottledCallback } from "use-debounce";
const names = [
"경미니",
"선주니",
"미누니",
"지누니",
"자라니",
"그러니",
"머라니",
"잼나니",
"웃기니",
];
function App() {
const renderedCount = useRef(0);
renderedCount.current++;
const [position, setPosition] = useState(window.scrollY);
const updatePosition = (e) => {
console.log(e.target.scrollTop);
setPosition(e.target.scrollTop);
};
// 추강
const scrollPosition = useThrottledCallback(updatePosition, 500);
return (
<>
<h1 style={{ textAlign: "center" }}>스크롤 이벤트 딜레이</h1>
<div
style={{
border: "3px solid pink",
color: "white",
textAlign: "center",
backgroundColor: "saddlebrown",
}}
>
<p>
딜레이 top위치{" "}
<span style={{ color: "purple", fontSize: "2em" }}>
{Number.parseInt(position)}
</span>
</p>
<p>
Rendering 몇번{" "}
<span style={{ color: "blue", fontSize: "2em" }}>
{renderedCount.current}
</span>
</p>
</div>
<div
style={{
height: 150,
border: "10px groove gold",
position: "relative",
overflowY: "scroll",
overflowX: "hidden",
}}
/* 바뀐 부분 updatePosition => scrollPosition */
onScroll={scrollPosition}
>
{names.map((name) => (
<h1 key={name} style={{ textAlign: "center" }}>
{name}
</h1>
))}
</div>
</>
);
}
export default App;
이제 실행해서 Rendering 카운트를 확인하면 이전 보다 훨씬 줄었음을 알수 있당. 그렇당
날이 시퍼렇게 선 예리함을 가진 마강이라면 이렇게 질문할 수 있다.
그거 꼭 써야해용? 여기 쓸 필요 없잖지 않지 않아요?
그렇당. 단지 위와 같은 소스는 으미가 없당.
스크롤 이벤트에 서버로 비동기 API 요청이 필요하다면, 그렇다면
그리고 그거이 서버에 무작위 부하를 줘서 힘들게 한다면 그때 사용하면 된당.
억지로 엉망진찬 현실적 합리적인 비유를 구지 억지로 내키지 않게 들자면
마강 서버의 능력이 1초에 한건씩 처리할 수 있는데, 초당 100건씩의 요청을
보내면 마강 서버는 망연자실 비실비실 피로감 찌든 망자가 될 수도 있당.
요럴때는 소영이( useDebouncedCallback 또는 useThrottledCallback)가 나서서
요청들을 줄을 세우고, 요청들 사이의 시간차를 벌려줘야 한당.(오켕?)
아래 링크에 가보면 추가적으로 좋은 정보가 더 담아져 있다. 때 되면 가보장
https://www.npmjs.com/package/use-debounce
시간을 거스리고 싶은 비단같은 마음은
비단 나만 가지고 있는 것은 아니거이다.
Time Slip 영화나 드라마가 딜레이를 두고 흥행이다.
머든 늘어진 반복 규칙으로 길어지면 지루하다.
그러면 비엔나 소세지 졸음이 풍년이다.
한쪽으로 치우쳐 흐르는 시간은 지루하다.
어쩌면 마강은 린과 닮았다가 안 닮았다.
우린 린과 함께 그린 세상에서
시간을 거슬러 잠에서 깨어난당.
https://www.youtube.com/watch?v=YwHVv3sGUpI
React(리액트) 티키타카 26 (bootstrap 아니면 tailwindcss) (0) | 2025.03.14 |
---|---|
React(리액트) 티키타카 25 (AG-Grid 활용 1) (0) | 2025.03.14 |
React(리액트) 티키타카 23 (useDebounce Hook 1) (2) | 2025.03.11 |
CSS 애니메이션 갑자기? (Spinner) (0) | 2025.03.07 |
React(리액트) 티키타카 22 (Redux -2) (0) | 2025.03.05 |