基于MERN栈的费用追踪App:实现总金额展示功能咨询
解决方案
1. 重构组件结构,将交易列表状态移至父组件
你的TranItem是单个交易项组件,无需维护transactions状态,该状态应放在父组件(比如TransactionDashboard)中统一管理,同时负责总金额计算与数据分析。
父组件示例(TransactionDashboard.js)
import React, { useState, useEffect } from 'react'; import TranItem from './TranItem'; export default function TransactionDashboard() { const [transactions, setTransactions] = useState([]); // 初始化获取所有交易数据 useEffect(() => { const fetchTransactions = async () => { const response = await fetch('http://localhost:5000/api/transaction/gettransactions', { method: "GET", headers: { "Content-Type": "application/json", "auth-token": localStorage.getItem('token') } }); const json = await response.json(); setTransactions(json); }; fetchTransactions(); }, []); // 删除交易方法,传递给子组件 const deleteTransaction = async (id) => { const response = await fetch(`http://localhost:5000/api/transaction/deletetransaction/${id}`, { method: "DELETE", headers: { "Content-Type": "application/json", "auth-token": localStorage.getItem('token') } }); await response.json(); // 更新本地交易列表 setTransactions(transactions.filter(tran => tran._id !== id)); }; // 计算账户总金额(根据交易类型区分收支) const calculateTotal = () => { return transactions.reduce((total, tran) => { return tran.type === "收入" ? total + parseFloat(tran.amount) : total - parseFloat(tran.amount); }, 0).toFixed(2); }; // 数据分析:统计总收入/总支出 const getIncomeExpenseStats = () => { const stats = { 收入: 0, 支出: 0 }; transactions.forEach(tran => { const amount = parseFloat(tran.amount); tran.type === "收入" ? stats.收入 += amount : stats.支出 += amount; }); return stats; }; // 数据分析:按标签统计金额 const getTagStats = () => { const tagStats = {}; transactions.forEach(tran => { const amount = parseFloat(tran.amount); const value = tran.type === "收入" ? amount : -amount; tagStats[tran.tag] = (tagStats[tran.tag] || 0) + value; }); return tagStats; }; const incomeExpenseStats = getIncomeExpenseStats(); const tagStats = getTagStats(); return ( <div className="container my-4"> {/* 总金额展示 */} <div className="card mb-4"> <div className="card-body"> <h4 className="card-title">账户总余额</h4> <p className="card-text fs-3">¥{calculateTotal()}</p> </div> </div> {/* 数据分析模块 */} <div className="row mb-4"> <div className="col-md-6"> <div className="card"> <div className="card-body"> <h5 className="card-title">收支统计</h5> <p className="card-text">总收入: ¥{incomeExpenseStats.收入.toFixed(2)}</p> <p className="card-text">总支出: ¥{incomeExpenseStats.支出.toFixed(2)}</p> </div> </div> </div> <div className="col-md-6"> <div className="card"> <div className="card-body"> <h5 className="card-title">按标签统计</h5> {Object.entries(tagStats).map(([tag, amount]) => ( <p key={tag} className="card-text"> {tag}: ¥{amount.toFixed(2)} </p> ))} </div> </div> </div> </div> {/* 交易列表 */} <div className="row"> {transactions.map(tran => ( <TranItem key={tran._id} gettran={tran} deleteTransaction={deleteTransaction} /> ))} </div> </div> ); }
2. 修改子组件TranItem.js
移除子组件内的transactions状态,直接调用父组件传递的删除方法:
import React from 'react'; export default function TranItem(props) { const { gettran, deleteTransaction } = props; return ( <> <div className='col-md-3'> <div className="card my-3"> <div className="card-body"> <h5 className="card-title">{gettran.type}</h5> <p className="card-text">¥{gettran.amount}</p> <p className="card-text">{gettran.tag}</p> <p className="card-text">{gettran.date}</p> <i className="fa-solid fa-trash mx-2" onClick={() => deleteTransaction(gettran._id)} /> <i className="fa-solid fa-pen mx-2" /> </div> </div> </div> </> ); }
关键说明
- 总金额计算:用
reduce遍历交易列表,根据交易类型(收入/支出)累加或扣除金额,最终保留两位小数。 - 数据分析功能:
- 收支统计:遍历交易分别统计总收入与总支出。
- 标签统计:按交易标签分组,计算每个标签下的净金额(收入为正、支出为负)。
- 状态管理:交易列表状态统一放在父组件,子组件仅负责渲染单个交易与触发删除操作,符合React单向数据流原则。
内容的提问来源于stack exchange,提问作者SCoder
相关产品推荐
相关产品推荐

