728x90

화면 크기를 구해주는 커스텀 훅을 만들게 된 이유는  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에서 호출만 하면 됩니다.

 

 

+ Recent posts