react의 장점 중 하나 dom을 직접 건드려도 되지 않아도 되는건데 굳이 알아야하나 라고 생각을 했지만 useRef는 리렌더링을 하지 않기 때문에 불필요한 데이터를 가져오지 않는다고 생각하여 사용하게 되었습니다.
프로젝트를 하게 되면 모달창을 만들 일이 많아지는데 처음에는 하나의 큰 div 태그를 위에 덮어주는 형식으로 만들어 useState로 켰다 껐다 하면서 만들었습니다. 하지만 이렇게 되면 너무 많은 state가 생겨 보기 불편하더라구요.
import { useState, useRef, useEffect } from 'react';
const Buttons = () => {
const [isOpenCategoryModal, setIsOpenCategoryModal] = useState(false);
const modalRef = useRef<HTMLDivElement>(null);
const handleOutsideClick = (event: any) => {
if (modalRef.current && !modalRef.current.contains(event.target)) {
setIsOpenCategoryModal(false);
}
};
useEffect(() => {
document.addEventListener('mousedown', handleOutsideClick);
return () => {
document.removeEventListener('mousedown', handleOutsideClick);
};
}, []);
return (
<S.ButtonContainer>
<S.ButtonBox>
<S.CategoryModalContainer>
<Button
title="카테고리"
onClick={() => {
setIsOpenCategoryModal(!isOpenCategoryModal);
}}
/>
{isOpenCategoryModal && (
<div ref={modalRef}>
<DropDown
dropDownList={CATEGORY_SORT}
clickValue={(list: string | number, index: number) => {
handleCategoryParams(list, index);
}}
width="10%"
$top="30"
// ref={modalRef}
/>
</div>
)}
</S.CategoryModalContainer>
</S.ButtonBox>
</S.ButtonContainer>
);
};
export default Buttons;
위 코드는 ref로 사용한 것입니다.
stlyed-components로 코드를 구현했기 떄문에 DropDown이 모달 컴포넌트 입니다.
하지만 위처럼 코드를 치면 굳이 모달 컴포넌트를 사용할 필요가 없겠죠?
DropDown컴포넌트를 사용하는 곳마다 함수를 정의하고 해야하니까 그래서 코드를 좀 수정해봤습니다.
라고 할라 했는데 타입에러가 나서 고치려고 하는데 몇시간 째 되지 않아서 일단 any로 만들었습니다.
// 부모컴포넌트
import { useState, useRef, useEffect } from 'react';
const Buttons = () => {
const [isOpenCategoryModal, setIsOpenCategoryModal] = useState(false);
return (
<S.ButtonContainer>
<S.ButtonBox>
<S.CategoryModalContainer>
<Button
title="카테고리"
onClick={() => {
setIsOpenCategoryModal(!isOpenCategoryModal);
}}
/>
{isOpenCategoryModal && (
<DropDown
dropDownList={CATEGORY_SORT}
clickValue={(list: string | number, index: number) => {
handleCategoryParams(list, index);
}}
width="10%"
$top="30"
ref={modalRef}
setState={setIsOpenCategoryModal}
/>
)}
</S.CategoryModalContainer>
</S.ButtonBox>
</S.ButtonContainer>
);
};
export default Buttons;
// 모달 컴포넌트
import { useEffect } from 'react';
import * as S from './DropDown.style';
interface Props {
dropDownList: List[];
modalRef: React.RefObject<HTMLDivElement>;
modalRef?: any;
setState?: any;
stateType?: string; //boolean만 state에 들어가는 것이 아니기 때문에 분기처리
}
interface List {
id: number;
title: string | number;
}
const DropDown = ({
dropDownList,
modalRef,
setState,
}: Props) => {
const handleOutsideClick = (event: any) => {
if (modalRef.current && !modalRef.current.contains(event.target)) {
stateType === 'boolean' ? setState(false) : setState('');
//boolean만 state에 들어가는 것이 아니기 때문에 분기처리
}
};
useEffect(() => {
document.addEventListener('mousedown', handleOutsideClick);
return () => {
document.removeEventListener('mousedown', handleOutsideClick);
};
}, []);
return (
<S.Container ref={modalRef}>
{dropDownList.map((list) => {
return (
<S.Text
key={list.id}
onClick={(e: React.MouseEvent) => {
e.stopPropagation();
clickValue(list.title, list.id);
}}
>
{list.title}
</S.Text>
);
})}
</S.Container>
);
};
export default DropDown;
이러면 끝이긴한데 타입스크립트는 로직을 짜는 것보다 타입 지정해주는 시간이 훨씬 오래 걸리는데 좀 더 능숙하게 다룰 때까지 공부를 해야겠습니당.
좋은 글 감사합니다. 자주 올게요 :)