가계부 + 다이어리
React - 테이블 행 추가
ssoyul
2024. 7. 5. 16:15
초기 전체 코드
import React, { useState } from "react";
import SelectBox from "../SelectBox/SelectBox";
import { getIncomeCategories, getSpendingCategories } from "../../Api/api.categories";
export default function AccountTable({ categories }) {
const [selectedCategory, setSelectedCategory] = useState("");
const [selectedSubCategory, setSelectedSubCategory] = useState("");
const [subCategories, setSubCategories] = useState([]);
const handleCategoryChange = (event) => {
const selected = event.target.value;
setSelectedCategory(selected);
if (selected === "1") {
getSpendingCategories()
.then(data => setSubCategories(data));
} else if (selected === "2") {
getIncomeCategories()
.then(data => setSubCategories(data));
} else {
setSubCategories([]);
}
}
//행 추가
const addRow = () => {
}
//행 삭제
const deleteRow = () => {
}
const handleSubCategoryChange = (event) => {
const subSelected = event.target.value;
setSelectedSubCategory(subSelected); // 변수명 변경
console.log("서브 카테고리:", subSelected);
}
return (
<>
<table>
<thead>
<tr>
<th>수입 / 지출</th>
<th>내역</th>
<th>금액</th>
<th>비고</th>
<th><button type="button" onClick={addRow}>추가</button></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<SelectBox
options={categories}
onChange={handleCategoryChange}
/>
</td>
<td>
<SelectBox
options={subCategories}
onChange={handleSubCategoryChange}
/>
</td>
<td><input /></td>
<td><input /></td>
<td><button type="button" onClick={deleteRow}>삭제</button></td>
</tr>
</tbody>
</table>
</>
);
}
실행 결과 화면
내가 원하는 것
1. 추가 버튼 클릭 시 <tr> 태그가 밑으로 하나씩 추가 됨
2. 삭제 버튼 클릭 시 해당 행이 삭제 됨
api는 관리를 위해 다른 api.js 폴더에 넣은 상태, 또한 첫 번째 카테고리를 불러오는 구간은 해당 컴포넌트의 부모에 있음
우선 하나의 행은 하나의 배열이라고 생각해서 그렇게 선언했다.
const [rows, setRows] = useState([
{ category: "", subCategory: "", recordAmount: "", recordDetails: "", subCategories: [] }
]);
첫 번째 카테고리의 값 가져오기
const handleCategoryChange = (event, index) => {
//선택한 카테고리 값 가져옴
const selected = event.target.value;
//현재 행 상태를 복사해서 새로운 배열 만듦
const newRows = [...rows];
//새로운 행 배열의 index위치에 있는 객체의 카테고리 속성을 selected 값으로 설정
newRows[index].category = selected;
if (selected === "1") {
getSpendingCategories()
.then(data => {
//해당 행의 서브 카테고리 업데이트
newRows[index].subCategories = data;
//업데이트된 행의 상태 결정
setRows(newRows);
});
} else if (selected === "2") {
getIncomeCategories()
.then(data => {
newRows[index].subCategories = data;
setRows(newRows);
});
} else {
newRows[index].subCategories = [];
setRows(newRows);
}
}
첫 번째 카테고리를 선택했을 시 결과값에 따라 두 번째 카테고리의 값이 불러와지게 만들기
(1이면 지출, 2라면 수입에 관련된 카테고리가 나오게끔)
const handleSubCategoryChange = (event, index) => {
const subSelected = event.target.value;
const newRows = [...rows];
//인덱스를 사용하여 특정 행의 서브 카테고리를 업데이트
newRows[index].subCategory = subSelected;
setRows(newRows);
console.log("서브 카테고리:", subSelected);
}
나머지 input 박스의 내용을 업데이트 하기 위한 함수
const handleInputChange = (event, index, field) => {
const newRows = [...rows];
newRows[index][field] = event.target.value;
setRows(newRows);
}
행 추가하는 버튼으로 작동할 함수
const addRow = () => {
//새로운 행 추가해서 행 상태 업데이트
//복사된 rows 행에 새로운 배열 전달하여 상태 업데이트
setRows([...rows, { category: "", subCategory: "", amount: "", remark: "", subCategories: [] }]);
}
이하 return문
return (
<>
<table>
<thead>
<tr>
<th>수입 / 지출</th>
<th>내역</th>
<th>금액</th>
<th>비고</th>
<th>
<button
type="button"
onClick={addRow}
>
<FontAwesomeIcon icon={faPlus} />
</button>
</th>
</tr>
</thead>
<tbody>
{rows.map((row, index) => (
<tr key={index}>
<td>
<SelectBox
options={categories}
onChange={(event) => handleCategoryChange(event, index)}
/>
</td>
<td>
<SelectBox
options={row.subCategories}
onChange={(event) => handleSubCategoryChange(event, index)}
/>
</td>
<td>
<input
value={row.recordAmount}
onChange={(event) => handleInputChange(event, index, "recordAmount")}
/>
</td>
<td>
<input
value={row.recordDetails}
onChange={(event) => handleInputChange(event, index, "recordDetails")}
/>
</td>
<td>
<button
type="button"
onClick={deleteRow}
>
<FontAwesomeIcon icon={faXmark} />
</button>
</td>
</tr>
))}
</tbody>
</table>
</>
);
실행 화면
전체 코드
import React, { useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faXmark, faPlus } from "@fortawesome/free-solid-svg-icons";
import SelectBox from "../SelectBox/SelectBox";
import { getIncomeCategories, getSpendingCategories } from "../../Api/api.categories.js";
export default function AccountTable({ categories }) {
const [rows, setRows] = useState([
{ category: "", subCategory: "", recordAmount: "", recordDetails: "", subCategories: [] }
]);
const handleCategoryChange = (event, index) => {
//선택한 카테고리 값 가져옴
const selected = event.target.value;
//현재 행 상태를 복사해서 새로운 배열 만듦
const newRows = [...rows];
//새로운 행 배열의 index위치에 있는 객체의 카테고리 속성을 selected 값으로 설정
newRows[index].category = selected;
if (selected === "1") {
getSpendingCategories()
.then(data => {
//해당 행의 서브 카테고리 업데이트
newRows[index].subCategories = data;
//업데이트된 행의 상태 결정
setRows(newRows);
});
} else if (selected === "2") {
getIncomeCategories()
.then(data => {
newRows[index].subCategories = data;
setRows(newRows);
});
} else {
newRows[index].subCategories = [];
setRows(newRows);
}
}
const handleSubCategoryChange = (event, index) => {
const subSelected = event.target.value;
const newRows = [...rows];
//인덱스를 사용하여 특정 행의 서브 카테고리를 업데이트
newRows[index].subCategory = subSelected;
setRows(newRows);
console.log("서브 카테고리:", subSelected);
}
const handleInputChange = (event, index, field) => {
const newRows = [...rows];
newRows[index][field] = event.target.value;
setRows(newRows);
}
//행 추가
const addRow = () => {
//새로운 행 추가해서 행 상태 업데이트
setRows([...rows, { category: "", subCategory: "", amount: "", remark: "", subCategories: [] }]);
}
//행 삭제
const deleteRow = () => {
}
return (
<>
<table>
<thead>
<tr>
<th>수입 / 지출</th>
<th>내역</th>
<th>금액</th>
<th>비고</th>
<th>
<button
type="button"
onClick={addRow}
>
<FontAwesomeIcon icon={faPlus} />
</button>
</th>
</tr>
</thead>
<tbody>
{rows.map((row, index) => (
<tr key={index}>
<td>
<SelectBox
options={categories}
onChange={(event) => handleCategoryChange(event, index)}
/>
</td>
<td>
<SelectBox
options={row.subCategories}
onChange={(event) => handleSubCategoryChange(event, index)}
/>
</td>
<td>
<input
value={row.recordAmount}
onChange={(event) => handleInputChange(event, index, "recordAmount")}
/>
</td>
<td>
<input
value={row.recordDetails}
onChange={(event) => handleInputChange(event, index, "recordDetails")}
/>
</td>
<td>
<button
type="button"
onClick={deleteRow}
>
<FontAwesomeIcon icon={faXmark} />
</button>
</td>
</tr>
))}
</tbody>
</table>
</>
);
}