화면 크기를 구해주는 커스텀 훅을 만들게 된 이유는 UI 라이브러리의 경우 간혹 max-width나 100% 같은 반응형 크기가 먹히지 않는 경우가 있습니다.
제가 사용하는 calendar , cropImage, antd 라이브러리 등등이 그러한데, 이 경우 화면의 크기를 구한후 해당 값을 props로 다시 전달해준다면 리랜더링을 통해서 반응형 처럼 구현할 수 있습니다.
그러기 위해선 우선적으로 화면의 크기를 구해야겠죠. 제가 두괄식 결론을 좋아해서 최종적으로 만든 코드부터 업로드 하겠습니다.
import { useEffect, useState } from 'react';
import { useDebounce } from '.';
export const useCalWindowWidth = () => {
const [windowSize, setWindowSize] = useState(window.innerWidth + '');
const debouncedValue = useDebounce(window.innerWidth + '', 200);
useEffect(() => {
const handleResize = () => {
setWindowSize(debouncedValue);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, [debouncedValue]);
return windowSize;
};
굳이 number 형태의 값을 추출할 필요도 없고(자바스크립트에서 암묵적 타입변환이 되기에), useDebounce훅의 타입을 string|number 로 변경할 경우 다른 컴퍼넌트에서 타입가드를 추가적으로 해야하기 때문에, 그냥 " "로 화면 크기를 타입변환 시켰습니다.
디바운스 관련 코드는 https://ungumungum.tistory.com/26
react input에 랜더링 제어를 위한 디바운스 커스텀훅 만들기
const AuthInput: React.FC = ({ title, password, setValue }) => { const [isVisible, setIsVisible] = useState(password); const [inputValue, setInputValue] = useState(''); const toggleIsPassword = () => { setIsVisible(!isVisible); }; const onChangeInput = (e:
ungumungum.tistory.com
디바운스를 걸고 싶지 않다면
import { useEffect, useState } from 'react';
export const useCalWindowWidth = () => {
const [windowSize, setWindowSize] = useState(window.innerWidth );
useEffect(() => {
const handleResize = () => {
setWindowSize(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return windowSize;
};
이렇게 코드를 작성하면 됩니다.
우선 화면 크기를 측정하는 훅의 원리를 설명하면
1. 우리는 화면의 크기를 구하면 된다. ( window.innerWidth)
2. 언제? 윈도우 화면의 이미지 크기가 바뀔 때 (window.addEventListener('resize' , ~~ )를 활용
3. 그때 바뀐 값을 저장할 곳 ( windowSize )
4. 언제까지 ? 이 페이지가 언마운트 될 때까지 (useEffect의 리턴문 활용)
5. 자주 쓰는 기능이니깐 커스텀 훅으로 만들자!
여기서 성능을 좋게하고 싶은데,, debounce나 throttle을 쓰면 좋겠다가. 제 생각의 흐름이었습니다.
window 객체에는 resize 이벤트가 있는데, 해당 이벤트가 일어날 때마다 windowSize에 해당 값을 set하면 됩니다.
이를 저는 handleResize라고 함수명을 지었습니다.
그렇기에 우선 값을 저장할 windowSize state를 만들고, 값이 바뀔 때 마다 다시 리랜더링되어서 값을 반환해야하니깐 useEffect훅을 사용했습니다. 이후 구글링을 해보니 resize시 특정 이벤트를 실행하려면에는 아래와 같이 작성하면 되더군요.
addEventListener("resize", (event) => {});
onresize = (event) => {};
https://developer.mozilla.org/en-US/docs/Web/API/Window/resize_event
Window: resize event - Web APIs | MDN
The resize event fires when the document view (window) has been resized.
developer.mozilla.org
제가 겪었던 트러블슈팅은.
처음엔 당연히 scroll 이벤트처럼 throttling이 맞지 않을까라고 생각했습니다. (이 와중에 handle의 타입도 제 생각과 다르더군요.. type of setTimeout 으로 타입을 지정하려고 했는데, 어차피 return 값은 setTimeout의 타입이라 생각했는데, 리턴값인 number를 입력해야 하더군요.. 근데 clearTimout의 매개변수가 setTimeout의 함수 이름인데 왜 함수 타입이 아니라 넘버 타입일까요..? 혹시 아시는 분 .. 댓글달아주세요 ㅠㅠ)
import { useEffect, useState } from 'react';
import { useDebounce } from '.';
export const useCalWindowWidth = () => {
const [windowSize, setWindowSize] = useState(window.innerWidth);
const [throttle, setThrottle] = useState(false);
useEffect(() => {
let handler: number;
const handleResize = () => {
if (throttle) return;
setThrottle(true);
handler = setTimeout(() => {
setWindowSize(window.innerWidth);
setThrottle(false);
}, 300);
setWindowSize(debouncedValue);
};
return () => {
window.removeEventListener('resize', handleResize);
clearTimeout(handler);
};
}, []);
return windowSize;
하지만 코드를 작성하고 생각해보니,, 결국 scroll 이벤트와 달리 저는 최종 화면에서의 window크기가 필요한데, 주기적으로 값을 구하는 throttle 방식은 옳지 않았던 것이죠..
그래서 0.2초안에 새 이벤트가 입력되지 않을때만 windowWidth를 반환하도록 debounce 훅을 사용했습니다.
사용방법은 사용하고 싶은 component에서 호출만 하면 됩니다.

'프론트엔드' 카테고리의 다른 글
react query v4 로 무한스크롤 구현하기+ 설명 (useInfinity Scroll) (0) | 2023.10.26 |
---|---|
리액트 useRef로 화면에 포커스 주는(화면 이동하는) 커스텀 훅 만들기(타입스크립트) (1) | 2023.10.14 |
리엑트 useEffect 훅의 역할 및 실행 순서 학습 및 실험 (0) | 2023.10.02 |
gitaction을 통한 빌드시 환경변수 적용하기 (0) | 2023.09.30 |
[React]이미지 미리보기 및 인코딩 하기(react-image-file-resizer -> 커스텀 훅만들기) (0) | 2023.09.17 |