[React] component로 만들어 재사용성 높여보기
들어가며
- 저번 시간에 "Mui" 를 통해 "Modal" 을 사용 하는 방법에 대해 설명 해 보았다.
- Modal, Button 특정 상황에만 한두번 사용 되지 않고 다양한 곳에서 사용되는 기능 들이 있다.
- 이러한 것들은 사용 할때마다 만들어 사용 하는 것 보다 component 로 따로 빼두면 코드의 재사용성이 높아지게 된다.
방법
1. BasicModal.tsx
- 우선 원하는 이름의(여기서는 "BasicModal.tsx") 파일을 만들어 준다.
- 그 후 저번 시간에 했던 Modal 의 코드를 넣어 준다.
- 이 때 Modal 내부에 들어갈 데이터와 open 여부, close 함수는 이 component 를 호출 하는 파일에서 보내 줄 것이다.
- 따라서 기존에 있던 useState 부분은 제거 해 준다.
- 또한 해당 데이터를 파라미터로 받아야 하기 때문에 데이터 들의 type 을 지정해 주는 type ModalBox 를 만들어 준다.
import Box from '@mui/material/Box';
import Modal from '@mui/material/Modal';
import { styled } from '@mui/material/styles';
const HeatmapGraphModalBox = styled(Box)(({ theme }) => ({
position: 'absolute' as 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 500,
[theme.breakpoints.down('sm')]: {
width: 300,
},
})) as typeof Box;
type ModalBox = { // 파라미터의 type 을 저장
data: string | JSX.Element;
open: boolean;
onClose: () => void;
};
// 기존의 open, onClose 를 호출 하는 부분으로 부터 받아서 사용
export default function BasicModal({ data, open, onClose }: ModalBox) {
return (
<Modal open={open} onClose={onClose}>
<HeatmapGraphModalBox>{data}</HeatmapGraphModalBox>
</Modal>
);
}
2. HeatmapGraph.tsx(기존에 사용하던 파일)
- 당연히 Modal 을 위한 style 은 제거 해 준다.
- 기존의 <Modal> tag 를 사용하던 방식과 유사하게 사용해 주면 된다.
- 이때 추가적으로 data 부분에 원하는 데이터를 넘겨 주면 된다.
- 아래는 BasicModal 을 호출 하는 부분이다.
<BasicModal
data={<HeatmapSvg width={'100%'} />} //넘겨서 Modal 에 띄우고자 하는 data
open={open} //Modal 의 state( 열렸는지 아닌지의 상태)
onClose={handleClose} //열린 modal 을 닫아주는 함수를 전달
></BasicModal>
- 이렇게 되면 data, open, onClose 가 function BasicModal({ data, open, onClose }: ModalBox) 의 data, open, onClose로 들어가게 되는 것이다.
전체 코드(HeatmapGraph.tsx만)
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import * as React from 'react';
import BasicModal from '@/components/common/BasicModal';
import { colors } from '@/theme/colors';
import { styled } from '@mui/material/styles';
const HeatmapGraphBox = styled(Box)({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
color: 'white',
gap: '0.5rem',
}) as typeof Box;
const MagnifyButton = styled(Button)({
paddingInline: '1rem',
color: 'white',
fontSize: '1rem',
}) as typeof Button;
type HeatmapGraphType = {
data: number[][];
color: string;
};
type HeatmapGraphModalBoxType = {
width: string;
};
export const HeatmapGraph = ({ data, color }: HeatmapGraphType) => {
const [open, setOpen] = React.useState(false);
function handleOpen() {
setOpen(true);
}
function handleClose() {
setOpen(false);
}
const squares = data.map((v, i) => {
return v.map((w, j) => {
return (
<rect
key={`heat_r_${i}_${j}`}
width="20"
height="18.333"
fill={colors[color]}
opacity={Math.sqrt(w / 3)}
x={22 * j + 2}
y={19.333 * i + 2}
/>
);
});
});
const HeatmapSvg = ({ width }: HeatmapGraphModalBoxType) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
// height="350"
fill="none"
viewBox="0 0 200 350"
>
{/* FIELD START */}
<path
fill="#254B1E"
d="M200 0H549.999V199.999H200z"
transform="rotate(90 200 0)"
></path>
<path
fill="#2B5C26"
d="M200 50H250V249.999H200z"
transform="rotate(90 200 50)"
></path>
<path
fill="#2B5C26"
d="M200 150H250V349.999H200z"
transform="rotate(90 200 150)"
></path>
<path
fill="#2B5C26"
d="M200 249.999H250V449.998H200z"
transform="rotate(90 200 249.999)"
></path>
<path
stroke="#C5C5C5"
strokeWidth="2"
d="M1 1H198.999V348.999H1z"
></path>
<path
stroke="#C5C5C5"
strokeWidth="2"
d="M103.125 77.75C60.169 77.75 25.296 43.472 24.756 1h156.737c-.54 42.472-35.412 76.75-78.368 76.75zM103.125 272.249c-42.956 0-77.829 34.278-78.369 76.75h156.737c-.54-42.472-35.412-76.75-78.368-76.75z"
></path>
<path
stroke="#C5C5C5"
strokeWidth="2"
d="M200 174.75L1.25 174.75"
></path>
<path
stroke="#C5C5C5"
strokeWidth="2"
d="M100 206.5c-17.397 0-31.5-14.103-31.5-31.5s14.103-31.5 31.5-31.5 31.5 14.103 31.5 31.5-14.103 31.5-31.5 31.5z"
></path>
<path
stroke="#C5C5C5"
strokeWidth="2"
d="M128.545 1H141.31799999999998V58.091H128.545z"
transform="rotate(90 128.545 1)"
></path>
<path
stroke="#C5C5C5"
strokeWidth="2"
d="M128.545 336.226H141.31799999999998V393.317H128.545z"
transform="rotate(90 128.545 336.226)"
></path>
{/* FIELD END */}
{/* HEATMAP START */}
{squares}
{/* HEATMAP END */}
</svg>
);
};
return (
<HeatmapGraphBox>
<HeatmapSvg width={'200'} />
<MagnifyButton onClick={handleOpen}>🔍 자세히 보기</MagnifyButton>
<BasicModal
data={<HeatmapSvg width={'100%'} />}
open={open}
onClose={handleClose}
></BasicModal>
</HeatmapGraphBox>
);
};
export default HeatmapGraph;
결과
- 코드 변경 전과 동일 한 결과가 나오는 것을 볼 수 있다.