리액트에서 리스트 화면을 출력할때 상위 행을 클릭하면 그에 해당하는 하위 행들을 노출하고 다시 한번 더 클릭하면 비노출 시키는 기능을 구현하고 싶었는데 난관에 부딪혔다. (부모 카테고리에 해당하는 자식 카테고리들을 노출)
문제 코드 )
export default function productCategoryList() {
const methods = useForm({
mode: "onChange",
});
const {
register,
control,
watch,
handleSubmit,
formState: { errors },
} = methods;
const { data } = useCategoryListQuery();
return (
<TableContainer>
<Table>
<TableHead>
<TableRow>
<StyledTableCellHead>카테고리</StyledTableCellHead>
</TableRow>
</TableHead>
{data && (
<TableBody>
{data.map((item) => (
<React.Fragment key={item.id}>
<TableRow>
<StyledTableCellHead>{item.name}</StyledTableCellHead>
</TableRow>
</React.Fragment>
))}
</TableBody>
)}
</Table>
</TableContainer>
);
}
<TableRow
onClick={() => toggleRow(item.id)}
style={{ cursor: "pointer" }}
>
const [expandedRows, setExpendedRows] = useState<number[]>([]);
const toggleRow = (rowId: number) => {
if (expandedRows.includes(rowId)) {
setExpendedRows(expandedRows.filter((id) => id !== rowId));
} else {
setExpendedRows([...expandedRows, rowId]);
}
};
클릭된 부모 행들의 상태를 관리하기 위해 expandedRows 배열을 만든다.(expandedRows는 하위 카테고리를 보여줄 상위 카테고리의 아이디만 담은 배열)
처음 페이지를 로드할때는 클릭된 아이템이 없으므로 expandedRows배열은 비어있게 된다.
그 이후 예를 들어 1번 아이템을 클릭했다면 expandedRows.includes조건이 false이므로 expandedRows에 rowId를 추가한다.
다시 한번 1번 아이템을 클릭한다면 이번에는 expandedRows.includes조건이 true이므로 expandedRows에 클릭된 rowId와 기존에 존재하는 id를 비교해서 같지 않은 경우 = 즉, 다시 한번 클릭되지 않은 경우만 filter 함수를 사용해서 남긴다.
{expandedRows.includes(item.id) &&
item.subCategory.map((i, index) => (
<TableRow key={index}>
<StyledTableCell>{i.name}</StyledTableCell>
</TableRow>
))}
expandedRows가 item.id(부모 카테고리 아이디)를 가지고 있다면 item.subCategory를 화면에 그리도록 처리했다.
해결된 코드)
export default function productCategoryList() {
const methods = useForm({
mode: "onChange",
});
const {
register,
control,
watch,
handleSubmit,
formState: { errors },
} = methods;
const { data } = useCategoryListQuery();
const [expandedRows, setExpendedRows] = useState<number[]>([]);
const toggleRow = (rowId: number) => {
if (expandedRows.includes(rowId)) {
setExpendedRows(expandedRows.filter((id) => id !== rowId));
} else {
setExpendedRows([...expandedRows, rowId]);
}
};
return (
<TableContainer>
<Table>
<TableHead>
<TableRow>
<StyledTableCellHead>카테고리</StyledTableCellHead>
</TableRow>
</TableHead>
{data && (
<TableBody>
{data.map((item) => (
<React.Fragment key={item.id}>
<TableRow
onClick={() => toggleRow(item.id)}
style={{ cursor: "pointer" }}
>
<StyledTableCellHead>{item.name}</StyledTableCellHead>
</TableRow>
{expandedRows.includes(item.id) &&
item.subCategory.map((i, index) => (
<TableRow key={index}>
<StyledTableCell>{i.name}</StyledTableCell>
</TableRow>
))}
</React.Fragment>
))}
</TableBody>
)}
</Table>
</TableContainer>
);
}