reactjs - 在反应中使用表单中的useState钩子时出错
问题描述
我在运行代码时遇到以下问题,需要一些帮助,因为即使在过去 4 天发现问题后我也无法找出问题所在。
第一个-根据我的 product_specs 钩子,它是两个输入字段的数组,即规范字段和值字段,当“http://localhost:3000/productupload”页面加载时,每个字段应该从一开始就存在,但我是没有从一开始就提交这两个文件。注意 -addClick 和 Remove Click 处理程序动态添加或删除这两个输入字段。表单加载时的网页图像附在下面。 第二-当我输入我的 sub_category 字段时,我收到错误 TypeError: Cannot set property 'sub_category' of undefined
我的产品上传页面代码是
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: "",
},
]);
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, i) => {
const { name, value } = e.target;
setSub_category({ ...sub_category, [name]: value });
setProduct_name({ ...product_name, [name]: value });
const p_specs = [...product_specs];
product_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 = () => {
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;
我的产品路线中的代码(使用 express 和 multer 进行图像上传
const express = require("express");
const router = express.Router();
const { check, validationResult } = require("express-validator");
const GridFsStorage = require("multer-gridfs-storage");
const multer = require("multer");
const connectDB = require("../../config/db");
const storage = new GridFsStorage({
db: connectDB(),
});
const upload = multer({ storage });
module.exports = upload;
// @route GET api/Product
// @desc Test route
// @acess Public
router.get("/", (req, res) => res.send("Product route"));
// @route GET api/Product
// @desc staff will upload product to database
// @acess Private
router.post(
"/",
[
check("subcategory", "subcategory is required").not().isEmpty(),
check("product name", "product name is required").not().isEmpty(),
],
upload.single("product_image"),
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
sub_category,
product_name,
product_image,
product_specs,
} = req.body;
try {
//See if product exists
let product = await Product.findOne({ product_name }); // Product.findOne() is mongo db query
if (product) {
return res
.status(400)
.json({ errors: [{ msg: "Product already exists" }] });
}
//New product instance will be created and once user.save() command is given user will be created in mongodb database.
product = new Product({
sub_category,
product_name,
product_image,
product_specs,
});
await product.save();
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
}
);
module.exports = router;
请帮助我,我被困在这个问题上 4 天了。请。
解决方案
谢谢大家的回复,我找到了解决办法。在这里分享我的最终代码。谢谢
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;
推荐阅读
- python - GRU 模型不学习
- python - sqlalchemy 合并/覆盖两个对象的字段
- c# - 独立输入模块中的 ProcessDrag 无法按预期工作
- python - re.findall 从 / 分隔的路径名中获取列表目录,但允许 // 作为文字,单个 /
- jquery - 如何(设置)使 Jquery 变量作为背景图像
- flutter - Flutter 函数以变量名结尾
- pentaho - 替换整个短语 - 替换为字符串 - Pentaho
- linux - yocto 在树莓派上构建后缺少 dhcp 包
- typescript - .eslintrc.js 显示错误:解析错误:“parserOptions.project”已为@typescript-eslint/parser 设置
- python - 初始化后如何通过鼠标拖动更改 tkinter 小部件的高度(或宽度)?