我正在用Redux工具包做我的PET项目,但是在输入方面有一些麻烦。当我添加一个输入点击它添加正确,但我不确定它添加在正确的地方(它应该添加在项目数组)。另一个问题是它的数据。如果我添加2个输入:1)产品名称:'Iphone 13',单位成本:'1200',单位:'2';2)产品名称:"Iphone 12",单位成本:"1100",单位:"1"。它只会添加最后一个数组长度只有1
为什么会这样?我做错了什么?
我已经尝试使用传播操作符,但它显示错误,当我点击添加项目按钮。
现在是代码
InvoicesList.js 带有INVOICES_LIST数组的文件,其中包含ITEMS,其中应该是来自输入的所有项。
export const INVOICES_LIST = [
{
id: Math.random().toString(),
number: Math.random().toFixed(2),
invoice_num: "#1232",
bill_from: "Pineapple Inc.",
bill_to: "REDQ Inc.",
total_cost: "14630",
status: "Pending",
order_date: "February 17th 2018",
bill_from_email: "pineapple@company.com",
bill_from_address: "86781 547th Ave, Osmond, NE, 68765",
bill_from_phone: "+(402) 748-3970",
bill_from_fax: "",
bill_to_email: "redq@company.com",
bill_to_address: "405 Mulberry Rd, Mc Grady, NC, 28649",
bill_to_phone: "+(740) 927-9284",
bill_to_fax: "+0(863) 228-7064",
ITEMS: [
{
item_name: "A box of happiness",
unit_costs: "200",
unit: "14",
price: "2800",
sub_total: "133300",
vat: "13300",
grand_total: "14630",
},
],
},
{
id: Math.random().toString(),
number: Math.random().toFixed(2),
invoice_num: "#1232",
bill_from: "AMD Inc.",
bill_to: "Intel Inc.",
total_cost: "14630",
status: "Delivered",
order_date: "February 17th 2018",
bill_from_email: "pineapple@company.com",
bill_from_address: "86781 547th Ave, Osmond, NE, 68765",
bill_from_phone: "+(402) 748-3970",
bill_from_fax: "",
bill_to_email: "redq@company.com",
bill_to_address: "405 Mulberry Rd, Mc Grady, NC, 28649",
bill_to_phone: "+(740) 927-9284",
bill_to_fax: "+0(863) 228-7064",
ITEMS: [
{
item_name: "Unicorn Tears",
unit_costs: "500",
unit: "14",
price: "1700",
sub_total: "133300",
vat: "13300",
grand_total: "14630",
},
],
},
{
id: Math.random().toString(),
number: Math.random().toFixed(2),
invoice_num: "#1232",
bill_from: "Apple Inc.",
bill_to: "Samsung",
total_cost: "14630",
status: "Shipped",
order_date: "February 17th 2018",
bill_from_email: "pineapple@company.com",
bill_from_address: "86781 547th Ave, Osmond, NE, 68765",
bill_from_phone: "+(402) 748-3970",
bill_from_fax: "",
bill_to_email: "redq@company.com",
bill_to_address: "405 Mulberry Rd, Mc Grady, NC, 28649",
bill_to_phone: "+(740) 927-9284",
bill_to_fax: "+0(863) 228-7064",
ITEMS: [
{
item_name: "Rainbow Machine",
unit_costs: "700",
unit: "5",
price: "3500",
sub_total: "133300",
vat: "13300",
grand_total: "14630",
},
],
},
];
AddInvoiceItem.js文件,您可以在其中找到输入和添加逻辑。
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { uiActions } from "../../store/ui-slice";
import classes from "./AddInvoiceItem.module.css";
import { useFormik } from "formik";
import Wrapper from "../../UI/Wrapper";
import Card from "../../UI/Card";
import Footer from "../../UI/Footer";
import Button from "../../UI/Button";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCalendar } from "@fortawesome/free-solid-svg-icons";
import { faEllipsis } from "@fortawesome/free-solid-svg-icons";
import { invoiceActions } from "../../store/invoice-slice";
import { Link } from "react-router-dom";
const AddInvoiceItem = (props) => {
const date = new Date();
const options = ["Pending", "Shipped", "Delivered"];
const inputs = [{ itemName: "", unitCosts: "", unit: "" }];
const [startDate, setStartDate] = useState(date);
const [selectedOption, setSelectedOption] = useState(options[0]);
const [listItems, setListItems] = useState(inputs);
const optionClickHandler = (value) => () => {
setSelectedOption(value);
dispatch(uiActions.toggleMoreOptions());
};
const addInvoiceHandler = (invoice) => {
console.log(invoice);
console.log(selectedOption);
dispatch(
invoiceActions.addNewInvoice({
id: Math.random(),
invoiceNumber: invoice.invoiceNumber,
billFrom: invoice.billFrom,
billFromAddress: invoice.billFromAddress,
billTo: invoice.billTo,
billToAddress: invoice.billToAddress,
status: selectedOption,
order_date: startDate.toJSON(),
ITEMS: [
{
id: Math.random(),
item_name: invoice.itemName,
unit_costs: invoice.unitCosts,
units: invoice.unit,
},
],
})
);
};
const formikInvoice = useFormik({
initialValues: {
invoiceNumber: "",
billFrom: "",
billFromAddress: "",
billTo: "",
billToAddress: "",
status: selectedOption,
date: startDate.toJSON(),
ITEMS: [
{
id: Math.random(),
itemName: "",
unitCosts: "",
unit: "",
},
],
},
onSubmit: (val) => {
addInvoiceHandler(val);
},
});
const dispatch = useDispatch();
const toggleMoreOptions = () => {
dispatch(uiActions.toggleMoreOptions());
};
const showOtherOptions = useSelector(
(state) => state.ui.selectMoreOptionsIsVisible
);
let counter = 1;
const addItemHandler = () => {
setListItems(listItems.concat([{ itemName: "", unitCosts: "", unit: "" }]));
};
return (
<form onSubmit={formikInvoice.handleSubmit}>
<Wrapper isShrinked={props.isShrinked}>
<Card>
<div className={classes.content}>
<div className={classes["buttons-wrapper"]}>
<Link to="/invoices">
<button type="button" className={classes["cancel-btn"]}>
Cancel
</button>
</Link>
{/* <Link to="/invoices"> */}
<Button className={classes["save-btn"]}>Save</Button>
{/* </Link> */}
</div>
<div className={classes["invoice-info-wrapper"]}>
<div className={classes["invoice-info"]}>
<h3>Invoice Info</h3>
<input
placeholder="Number"
type="text"
name="invoiceNumber"
id="invoiceNumber"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.invoiceNumber}
onBlur={formikInvoice.handleBlur}
></input>
</div>
<div className={classes["right-side-column"]}>
<div className={classes["order-status"]}>
<span>Order Status: </span>
<div className={classes.buttons}>
{showOtherOptions && (
<ul className={classes.options}>
{options.map((option) => (
<li
onClick={optionClickHandler(option)}
key={Math.random()}
>
{option}
</li>
))}
</ul>
)}
<button type="button" className={classes.status}>
{selectedOption}
</button>
<button
type="button"
className={classes.dots}
onClick={toggleMoreOptions}
>
<FontAwesomeIcon icon={faEllipsis} />
</button>
</div>
</div>
<div className={classes["order-date"]}>
<span>Order Date:</span>
<DatePicker
className={classes["order-date-input"]}
selected={startDate}
onChange={(val) => setStartDate(val)}
/>
<FontAwesomeIcon
icon={faCalendar}
className={classes.calendar}
></FontAwesomeIcon>
</div>
</div>
</div>
<div className={classes["order-bills"]}>
<div className={classes["bill-from"]}>
<input
placeholder="Bill From"
type="text"
name="billFrom"
id="billFrom"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.billFrom}
onBlur={formikInvoice.handleBlur}
></input>
<textarea
placeholder="Bill From Address"
name="billFromAddress"
id="billFromAddress"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.billFromAddress}
onBlur={formikInvoice.handleBlur}
></textarea>
</div>
<div className={classes["bill-to"]}>
<input
placeholder="Bill To"
type="text"
name="billTo"
id="billTo"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.billTo}
onBlur={formikInvoice.handleBlur}
></input>
<textarea
placeholder="Bill To Address"
name="billToAddress"
id="billToAddress"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.billToAddress}
onBlur={formikInvoice.handleBlur}
></textarea>
</div>
</div>
<div className={classes["table-wrapper"]}>
<table>
<colgroup>
<col className={classes.col1}></col>
<col className={classes.col2}></col>
<col className={classes.col3}></col>
<col className={classes.col4}></col>
<col className={classes.col5}></col>
<col className={classes.col6}></col>
</colgroup>
<thead>
<tr>
<td className={classes["more-padding"]}>#</td>
<td>Item Name</td>
<td>Unit Costs</td>
<td>Unit</td>
<td>Price</td>
<td></td>
</tr>
</thead>
<tbody>
{listItems.map((item, index) => (
<tr key={index}>
<td className={classes["more-padding"]}>{counter++}</td>
<td>
<input
placeholder="Item Name"
className={classes.inputs}
name="itemName"
id="itemName"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.item_name}
onBlur={formikInvoice.handleBlur}
></input>
</td>
<td>
<input
placeholder="Unit Costs"
className={classes.inputs}
name="unitCosts"
id="unitCosts"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.unit_costs}
onBlur={formikInvoice.handleBlur}
></input>
</td>
<td>
<input
placeholder="Unit"
className={classes.inputs}
name="unit"
id="unit"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.units}
onBlur={formikInvoice.handleBlur}
></input>
</td>
<td>0</td>
<td></td>
{/* There should be dynamic values later */}
</tr>
))}
</tbody>
</table>
<div className={classes["add-item-btn"]}>
<button
onClick={addItemHandler}
type="button"
className={classes["add-item-btn"]}
>
Add Item
</button>
</div>
<div className={classes.total}>
<p className={classes["sub-total"]}>
<span>Sub Total: </span>
<span>$0</span>
{/* Dynamic value later here */}
</p>
<div className={classes["total-vat"]}>
<span>Total Vat:</span>
<div className={classes["total-sum"]}>
<span className={classes["input-wrapper"]}>
<input type="text" defaultValue="10"></input>
<span>%</span>
</span>
<span className={classes.sum}>$0</span>
{/* Dynamic value later here */}
</div>
</div>
<div className={classes["grand-total"]}>
<h3>Grand Total</h3>
<div className={classes.input}>
<input type="text" defaultValue="$"></input>
<span>0</span>
{/* Dynamic value later here */}
</div>
</div>
</div>
</div>
<div className={classes.dummy}></div>
</div>
</Card>
<Footer />
</Wrapper>
</form>
);
};
export default AddInvoiceItem;
invoice-slice.js
文件。import { createSlice } from "@reduxjs/toolkit";
import { INVOICES_LIST } from "../Pages/Invoice/InvoicesList";
const invoiceSlice = createSlice({
name: "invoice",
initialState: {
invoices: INVOICES_LIST,
},
reducers: {
addNewInvoice(state, action) {
const newItem = action.payload;
state.invoices.push({
id: newItem.id,
billFrom: newItem.bill_from,
billFromAddress: newItem.billFromAddress,
billTo: newItem.bill_to,
billToAddress: newItem.bill_to_address,
invoiceNumber: newItem.invoice_num,
status: newItem.status,
order_date: newItem.order_date,
ITEMS: [
{
id: Math.random(),
item_name: newItem.item_name,
unit_costs: newItem.unit_costs,
units: newItem.units,
},
],
});
console.log(newItem);
},
removeInvoice(state, action) {
const id = action.payload;
state.invoices = state.invoices.filter((item) => item.id !== id);
},
editInvoice() {},
},
});
export const invoiceActions = invoiceSlice.actions;
export default invoiceSlice;
。我应该给每个物品一个唯一的id吗?因为,我不确定我是否需要。
注:这里是github的repo - https://github.com/stepan-slyvka/test-project
P.P.S.这是我的PET项目,所以不要奇怪为什么我在一个页面和另一个Redux中使用上下文。这只是为了学习。
我已经看过你的代码了。这是使它全部工作的一种方法,但是,一般来说,您可能应该决定是否使用formik的状态作为listtitem的主要状态,并按照该方向重构代码或做其他事情。
然而,这里有一个粗略的方法来解决你的问题,把它作为决策如何管理它的参考。
将以下内容添加到您的AddinvoiceItem.js中:
const updateItemHandler = (index, inputName, value) => {
listItems[index] = {...listItems[index], [inputName]: value};
}
const updateValuesOnSubmit = () => {
return listItems;
}
并更改每个输入(itemName, unitCosts, unit)以使用其onChange参数中的新函数:
onChange={(e) => updateItemHandler(index, 'THIS has to be the name of the input, for example itemName', e.currentTarget.value)}
更改addInvoiceHandler以在其分派中使用新函数:
dispatch(
invoiceActions.addNewInvoice({
id: Math.random(),
invoiceNumber: invoice.invoiceNumber,
billFrom: invoice.billFrom,
billFromAddress: invoice.billFromAddress,
billTo: invoice.billTo,
billToAddress: invoice.billToAddress,
status: selectedOption,
order_date: startDate.toJSON(),
ITEMS: [
...updateValuesOnSubmit()
],
})
);
};
最后,转到invoice-slice.js并执行以下操作:
ITEMS: [
...newItem.ITEMS
],
这就是您应该如何能够获得发票中的多个项目到您的redux状态。让我再次强调,你仍然应该考虑使用Formik的状态和它的管理,handleChange功能等,因为你已经为这个任务选择了它。
但这仍然是做你想做的事情的一种方法。希望能有所帮助!