가계부 + 다이어리

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>
        </>
    );
}