I'm trying to add a new object to my array when I click on a button to add a new row to my form.
import React, { useState, useEffect } from 'react';
import NumberFormat from 'react-number-format';
import { Button, Form, Label, Input } from 'reactstrap';
import RctCollapsibleCard from './RctCollapsibleCard/RctCollapsibleCard';
import LinearProgress from '../util/LinearProgress';
import * as tenantAPI from '../api/tenants';
const Test = (props) => {
const tenantName = props.tenantName;
const tenantTransactionID = props.selectedTransaction;
const propertyID = props.propertyID;
const [ loading, setLoading ] = useState(true);
const [ updated, setUpdated ] = useState(false);
const [ payments, setPayments ] = useState([]);
const [ categories, setCategories ] = useState([]);
const [ paymentAmount, setPaymentAmount ] = useState(0);
const [ currentTotal, setCurrentTotal ] = useState(0);
const [ showSave, setShowSave ] = useState(false);
useEffect(() => {
async function fetchData() {
console.log('load');
setLoading(true);
setPaymentAmount(parseFloat(await tenantAPI.getTransactionAmount(tenantTransactionID)));
//const allocated = await tenantAPI.getAllocatedPayments(tenantTransactionID);
//setPayments(allocated);
setCategories(await tenantAPI.getPaymentCategories(propertyID));
//let total = 0;
//for(const a of allocated) {
//total += parseFloat(a.PaymentAmount);
//}
//setCurrentTotal(total);
setLoading(false);
}
fetchData();
}, [tenantTransactionID, propertyID])
useEffect(() => {
console.log(payments);
if(parseFloat(paymentAmount).toFixed(2) === parseFloat(currentTotal).toFixed(2)) {
setShowSave(true);
} else {
setShowSave(false);
}
}, [updated]);
const handleCategoryChange = (val, index) => {
}
const handleAmountChange = (val, index) => {
}
const handleDelete = (index) => {
}
const addPayment = () => {
setPayments([
...payments,
{
PaymentAmount: 0,
CategoryID: 0
}
]);
console.log(payments);
setUpdated(!updated);
}
const save = () => {
alert('Save BTN');
}
const render = () => {
if(loading) {
return (
<RctCollapsibleCard
colClasses="col-xs-12 col-sm-12 col-md-12"
heading={"Loading Allocate Payment..."}
>
<LinearProgress />
</RctCollapsibleCard>
);
} else {
const showSaveBTN = () => {
if(showSave) {
return (
<>
<Button type="button" color="primary" onClick={save}>Save</Button>
{' '}
<Button color="warning" onClick={() => props.setOpenAllocate(false)}>Cancel</Button>
</>
);
} else {
return <Button color="warning" onClick={() => props.setOpenAllocate(false)}>Cancel</Button>;
}
}
const heading = `Allocating ${tenantName} payment of ${<NumberFormat displayType={'text'} value={paymentAmount} thousandSeparator={true} prefix={'$'} />}`;
return (
<>
<div className="row">
<div className="col-sm-12 col-md-12 col-xl-12">
<RctCollapsibleCard heading={heading}>
<Form>
{payments.map((element, index) => {
//console.log(element);
//console.log(index);
return (
<div className="row">
<div className="col-sm-4">
<Label className="mr-sm-10">Payment Category</Label>
<Input type="select"
value={element.CategoryID} onChange={(e) => handleCategoryChange(e.target.value, index)}
>
<option value="0">Select</option>
{categories.map((obj) => {
return (
<option
key={obj.PaymentsCategoryID}
value={obj.PaymentsCategoryID}
>
{obj.Category}
</option>
);
})}
</Input>
</div>
<div className="col-sm-4">
<Label className="mr-sm-10">Amount</Label>
<NumberFormat value={parseFloat(element.PaymentAmount).toFixed(2)} thousandSeparator={true} prefix={'$'}
onChange={(e) => handleAmountChange(e.value, index)} className="form-group"
/>
</div>
<div className="col-sm-3">
<Button type="button" color="danger" size="sm" className="w-auto" style={{marginTop: '10px'}}
onClick={handleDelete(index)}
>
Delete
</Button>
</div>
</div>
);
})}
<div className="row">
<div className="col-sm-4">
<Label className="mr-sm-10">Payment Category</Label>
<Input type="select" onChange={(e) => handleCategoryChange(e.target.value, -1)}>
<option value="0">Select</option>
{categories.map((obj) => {
return (
<option
key={obj.PaymentsCategoryID}
value={obj.PaymentsCategoryID}
>
{obj.Category}
</option>
);
})}
</Input>
</div>
<div className="col-sm-4">
<Label className="mr-sm-10">Amount</Label>
<NumberFormat thousandSeparator={true} prefix={'$'} className="forms-group"
onChange={(e) => handleAmountChange(e.value, -1)}
/>
</div>
</div>
<div className="row">
<div className="col-sm-2" style={{marginTop: '10px'}}>
<Button type="button" className="btn" onClick={() => addPayment()}>Add a Payment</Button>
</div>
</div>
<div className="row">
<div className="col-sm-12" style={{marginTop: '10px'}}>
<p>
<span>Payment Total: <NumberFormat displayType={'text'} value={paymentAmount} thousandSeparator={true} prefix={'$'} /></span>
<br />
<span style={{color: 'red'}}>Allocated Amount: <NumberFormat displayType={'text'} value={currentTotal} thousandSeparator={true} prefix={'$'} /></span>
</p>
</div>
</div>
{showSaveBTN()}
</Form>
</RctCollapsibleCard>
</div>
</div>
</>
);
}
}
return render();
}
export default Test;
Is there a better (more appropriate) way to manage add payments row (with the select and input) when the user click on the Add Payment button?
Thanks
The reason why payments
is never more than one element is two fold:
addPayment
which has a closure around payment
and thus only ever refers to the version of the payment
state when the addPayment
function was defined in that particular render.React controls state and more in memory, so by refreshing the page, you've lost most of what react is good for.
Here's an example (based on your code) that shows how to work with data without refreshing.
const { useState, useEffect, useMemo } = React; const App = () => { // This is the method to add a new payment to the array (it default the value to 0) const addPayment = () => { setPayments([...payments, { PaymentAmount: 5, CategoryID: 0, }, ]); }; // The payments array is where I will keep the payment category ID and Amount const [payments, setPayments] = useState([]); // I have a useEffect to reload the page and do some operations based on the updated variable useEffect(() => { // Perform some effects based on the payments here // If payments is in the dependency array, this function will be called with the latest payments every time it changes. console;log(payments), }; [payments]). // Perform memoized calculation on payments const totalPayment = useMemo( () => payments,reduce((sum. current) => sum + current,PaymentAmount, 0); [payments] ); const categories = []: // I have a Form where I render the payments return ( <div> <button onClick={addPayment}>Add</button> <span>Current Total. {totalPayment}$</span> <form> {payments,map((element: index) => ( <div> Payment #{index}. ${element;PaymentAmount}{" "} </div> ))} </form> </div> ); }. ReactDOM,render(<App />. document;getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script> <div id="root" />
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.