[英]Array of object iterating through map to render a component key not updating in props
[英]object.key for using object as array on map not allwoing to pass argument as props to React component
代碼和框的鏈接- https://codesandbox.io/s/productupload-form-krovj?fontsize=14&hidenavigation=1&theme=dark
在 productupload react 組件中,我使用的是product_specs useState 鈎子,它是數組,但由於其他 useStates 是 object 並且只有 product_specs useState 是數組,因此很難在此實現 handleChange 事件處理程序。 因此,我將 product_specs 更改為 object 而不是數組。 As i cant use map method on object, I have used Object.key but now issue is, I am not able to use argument named as item inside map and pass it to react 零件。 它給出了錯誤,因為無法讀取未定義的屬性長度。 因為我使用 item.specification 作為組件的道具。 有人可以幫助解決這個問題。 注意我不能對組件代碼進行任何更改,因為許多其他組件都依賴於它。
productupload 和 forminput 組件的代碼都在下面給出。
import React, { useState } from "react";
import FormInput from "../Forminput/forminput";
import CustomButton from "../Custombutton/custombutton";
import axios from "axios";
const ProductUpload = () => {
const [sub_category, setSub_category] = useState("");
const [product_name, setProduct_name] = useState("");
const [product_image, setProduct_image] = useState("");
const [product_specs, setProduct_specs] = useState([
{
specification: "",
specvalue: "",
},
]);
//note VIMP to add square bracket for array
const imageSelectedHandler = (event) => {
setProduct_image({ product_image: event.target.files[0] });
};
// const imageUploadHandler = () => {
// const fd = new FormData();
// fd.append("product_image", product_image, product_image.name); //.name is Imp as name is property of file
// };
const handleChange = (e) => {
const { name, value } = e.target;
setSub_category({ ...sub_category, [name]: value });
setProduct_name({ ...product_name, [name]: value });
setProduct_specs({ ...product_specs, [name]: value });
// const p_specs = [...product_specs];
// p_specs[i][name] = value;
// setProduct_specs(p_specs); //This is 100% right.
console.log(product_specs);
};
//to add extra input field
const addClick = () => {
// setProduct_specs([...product_specs, { specification: "", specvalue: "" }]); //this is 100% right
//also 100% correct alternative to above line
const p_specs = [...product_specs];
p_specs.push({ specification: "", specvalue: "" });
setProduct_specs(p_specs);
};
//to remove extra input field
const removeClick = (i) => {
const p_specs = [...product_specs];
p_specs.splice(i, 1);
setProduct_specs(p_specs);
};
const handleSubmit = async (event) => {
event.preventDefault();
const newProduct = {
sub_category,
product_name,
product_image,
product_specs,
};
try {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify(newProduct);
const res = await axios.post("/api/product", body, config);
console.log(res.data);
} catch (error) {
console.error(error.response.data);
}
};
const createUI = () => {
return Object.keys(product_specs).map((item, i) => {
return (
<div key={i} className="inputgroup">
<FormInput
type="text"
name="specification"
handleChange={(e) => handleChange(e, i)}
value={item.specification}
label="specification"
required
customwidth="300px"
></FormInput>
<FormInput
type="text"
name="specvalue"
handleChange={(e) => handleChange(e, i)}
value={item.specvalue}
label="specification values seperated by quomas"
required
></FormInput>
<CustomButton
onClick={() => removeClick(i)}
type="button"
value="remove"
style={{ margin: "12px" }}
>
Remove
</CustomButton>
</div>
);
});
};
return (
<div className="container">
<form
action="/upload"
method="post"
className="form"
onSubmit={handleSubmit}
encType="multipart/form-data"
>
<h3 style={{ color: "roboto, sans-serif" }}>
Add new product to the database
</h3>
<div
style={{
display: "flex",
height: "200px",
width: "200px",
border: "2px solid #DADCE0",
borderRadius: "6px",
position: "relative",
}}
>
<input
// style={{ display: "none" }}
type="file"
accept="image/*"
onChange={imageSelectedHandler}
multiple={false}
name="product_image"
/>
{/* <CustomButton >
Select Image
</CustomButton>
<CustomButton
// onClick={imageUploadHandler}
>
Upload
</CustomButton> */}
{/*as per brad- type = "submit" value="submit" this should not be used, file should upload with the form submit */}
<div>
<img
style={{
width: "100%",
height: "100%",
}}
alt="#"
/>
</div>
</div>
<FormInput
type="text"
name="sub_category"
handleChange={handleChange}
value={sub_category}
label="select from subcategories"
required
></FormInput>
<FormInput
type="text"
name="product_name"
handleChange={handleChange}
value={product_name}
label="product name"
required
></FormInput>
{createUI()}
<div>
<CustomButton
onClick={addClick}
type="button"
style={{ margin: "14px" }}
>
Add More Fields
</CustomButton>
<CustomButton type="submit" style={{ margin: "12px" }}>
Upload Product
</CustomButton>
</div>
</form>
</div>
);
};
export default ProductUpload;
FormInput 組件的代碼
import "./forminput.scss";
const FormInput = ({
handleChange,
label,
customwidth,
// register, //useForm hook property to handel change automatically
...otherProps
}) => (
<div className="group" style={{ width: customwidth }}>
<input
className="form-input"
onChange={handleChange}
// ref={register}
{...otherProps}
/>
{label ? (
<label
className={`${
otherProps.value.length ? "shrink" : ""
} form-input-label`}
>
{label}
</label>
) : null}
</div>
);
export default FormInput;
我看到您在 handleChange function 中使用i
來了解 product_specs 的索引。 但是對於 product_name 或 product_category 等其他狀態, i
的值是未定義的。 所以我認為你可以檢查i
是否未定義。
const handleChange = (e, i) => {
console.log(e.target.value, i);
const { name, value } = e.target;
setSub_category({ ...sub_category, [name]: value });
setProduct_name({ ...product_name, [name]: value });
const p_specs = [...product_specs];
if(i) {
product_specs[i][name] = value;
setProduct_specs(p_specs); //This is 100% right.
}
};
而且我認為它不會影響其他組件,因為i
只會對 product_specs 有價值。
嘿伙計們這很容易我不需要使用任何 Object.key。 可能有很多選擇,但就我而言,我認為這是最簡單的方法。 在這里分享我的最終代碼。
import FormInput from "../Forminput/forminput";
import CustomButton from "../Custombutton/custombutton";
import axios from "axios";
const ProductUpload = () => {
const [sub_category, setSub_category] = useState({ sub_category: "" });
const [product_name, setProduct_name] = useState({ product_name: "" });
const [product_image, setProduct_image] = useState("");
const [product_specs, setProduct_specs] = useState([
{
specification: "",
specvalue: "",
},
]);
//note VIMP to add square bracket for array
const imageSelectedHandler = (event) => {
setProduct_image({ product_image: event.target.files[0] });
};
// const imageUploadHandler = () => {
// const fd = new FormData();
// fd.append("product_image", product_image, product_image.name); //.name is Imp as name is property of file
// };
const handleChange1 = (e) => {
const { name, value } = e.target;
setSub_category({ ...sub_category, [name]: value });
setProduct_name({ ...product_name, [name]: value });
};
const handleChange2 = (e, i) => {
const { name, value } = e.target;
const p_specs = [...product_specs];
p_specs[i][name] = value;
setProduct_specs(p_specs); //This is 100% right.
};
//to add extra input field
const addClick = () => {
// setProduct_specs([...product_specs, { specification: "", specvalue: "" }]); //this is 100% right
//also 100% correct alternative to above line
const p_specs = [...product_specs];
p_specs.push({ specification: "", specvalue: "" });
setProduct_specs(p_specs);
};
//to remove extra input field
const removeClick = (i) => {
const p_specs = [...product_specs];
p_specs.splice(i, 1);
setProduct_specs(p_specs);
};
const handleSubmit = async (event) => {
event.preventDefault();
const newProduct = {
sub_category,
product_name,
product_image,
product_specs,
};
try {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify(newProduct);
const res = await axios.post("/api/product", body, config);
console.log(res.data);
} catch (error) {
console.error(error.response.data);
}
};
const createUI = () => {
return product_specs.map((item, i) => {
return (
<div key={i} className="inputgroup">
<FormInput
type="text"
name="specification"
handleChange={(e) => handleChange2(e, i)}
value={item.specification}
label="specification"
required
customwidth="300px"
></FormInput>
<FormInput
type="text"
name="specvalue"
handleChange={(e) => handleChange2(e, i)}
value={item.specvalue}
label="specification values seperated by quomas"
required
></FormInput>
<CustomButton
onClick={() => removeClick(i)}
type="button"
value="remove"
style={{ margin: "12px" }}
>
Remove
</CustomButton>
</div>
);
});
};
return (
<div className="container">
<form
action="/upload"
method="post"
className="form"
onSubmit={handleSubmit}
encType="multipart/form-data"
>
<h3 style={{ color: "roboto, sans-serif" }}>
Add new product to the database
</h3>
<div
style={{
display: "flex",
height: "200px",
width: "200px",
border: "2px solid #DADCE0",
borderRadius: "6px",
position: "relative",
}}
>
<input
// style={{ display: "none" }}
type="file"
accept="image/*"
onChange={imageSelectedHandler}
multiple={false}
name="product_image"
/>
{/* <CustomButton >
Select Image
</CustomButton>
<CustomButton
// onClick={imageUploadHandler}
>
Upload
</CustomButton> */}
{/*as per brad- type = "submit" value="submit" this should not be used, file should upload with the form submit */}
<div>
<img
style={{
width: "100%",
height: "100%",
}}
alt="#"
/>
</div>
</div>
<FormInput
type="text"
name="sub_category"
handleChange={handleChange1}
value={sub_category.sub_category}
label="select from subcategories"
required
></FormInput>
<FormInput
type="text"
name="product_name"
handleChange={handleChange1}
value={product_name.product_name}
label="product name"
required
></FormInput>
{console.log(product_name)}
{console.log(product_specs)}
{createUI()}
<div>
<CustomButton
onClick={addClick}
type="button"
style={{ margin: "14px" }}
>
Add More Fields
</CustomButton>
<CustomButton type="submit" style={{ margin: "12px" }}>
Upload Product
</CustomButton>
</div>
</form>
</div>
);
};
export default ProductUpload;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.