React 钩子更新状态将对象添加到数组

时间:2021-05-10 19:49:23

标签: reactjs react-hooks

当我单击按钮向表单添加新行时,我正在尝试向数组添加新对象。

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;

当用户单击“添加付款”按钮时,是否有更好(更合适)的方式来管理添加付款行(使用选择和输入)?

谢谢

1 个答案:

答案 0 :(得分:1)

callback_query.message 永远不会超过一个元素的原因有两个:

  1. 它被登录到 payments 中,它在 addPayment 周围有一个闭包,因此当 payment 函数在其中定义时,它只引用 payment 状态的版本特定渲染。
  2. 更大的问题:您在更改本地状态(在内存中)后刷新页面。 React 状态在内存中,这意味着无论何时刷新页面,它都会丢失(除非您在会话/本地存储中存储/检索它)。

React 在内存中控制状态和更多内容,因此通过刷新页面,您已经失去了 React 的大部分优势。

这是一个示例(基于您的代码),展示了如何在不刷新的情况下处理数据。

addPayment
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"));