状态更改时组件不会重新渲染
我正在尝试在反应函数组件中使用更新状态,但它不会立即重新渲染。
我的应用程序输入每个产品的材料成本并计算该产品的总成本。我希望在输入更新后重新渲染组件以显示更新的总数。似乎每次材料成本更改时都会计算总计,并且状态都会更新,但我无法让显示的总计显示新金额。
这是我的应用程序的屏幕截图(为简单起见,删除了表中元素的一些代码)
export let data =
{
name: "Name",
description:
"",
products: [
{
id: 1,
name: "Name 1",
material: 1.05,
time: 25,
total: 0,
},
{
id: 2,
name: "Name 2",
material: 3,
time: 252,
total: 0,
},
],
}
这是父组件。它具有主渲染以及 setTotal 和 setMaterial 函数。渲染映射通过每个productData.products,以便子组件可以渲染它们。
setTotal 和 setMaterial 函数几乎相同(我确信有一种方法可以重构它)。这段代码似乎可以很好地更新状态,尽管我确实注意到在这些函数中执行 console.log() 不起作用。
function CompareCard({ type, labor }) {
const [productData, setProductData] = useState(data);
const setTotal = () => {
function setTotalUpdate(id, total) {
const productPrevious = productData.products.find(function (rec) {
return rec.id === id;
});
const productUpdated = {
...productPrevious,
total: total,
};
const productNew = productData.products.map(function (rec) {
return rec.id === id ? productUpdated : rec;
});
const productFullNew = {
...productData,
products: productNew,
};
setProductData(productFullNew);
}
};
const setMaterial = () => {
function setMaterialUpdate(id, materialCost) {
const productPrevious = productData.products.find(function (rec) {
return rec.id === id;
});
const productUpdated = {
...productPrevious,
material: materialCost,
};
const productNew = productData.products.map(function (rec) {
return rec.id === id ? productUpdated : rec;
});
console.log("productsNew:", productNew);
const productFullNew = {
...productData,
products: productNew,
};
setProductData(productFullNew);
}
};
return (
<div className="Card">
<h3> {productData.name} </h3>
<p>{productData.description}</p>
<table className="table">
<thead>
<tr>
<th scope="col">
<p>
<b> Material </b>
</p>
</th>
<th scope="col">
<p>
<b> Labor </b>
</p>
</th>
<th scope="col">
<p>
<b> Total</b>
</p>
</th>
</tr>
</thead>
<tbody id={`${productData.name}`}>
{productData.products.map((product) => {
return (
<Products
key={product.id}
product={product}
labor={3}
setMaterial={setMaterial}
setTotal={setTotal}
/>
);
})}
</tbody>
</table>
</div>
);
}
export default CompareCard;
这是子元素。它单独处理每个产品。
第一个 calcTotal() 用于设置原始总计。此调用有效,但当材质稍后更改时,总计不会继续重新渲染。
import React, { useState, useEffect } from "react";
function Products({ product, labor, setMaterial, setTotal }) {
function setMaterialState(newMaterial) {
product.material = newMaterial;
}
function calcTotal() {
product.total = labor + product.material;
}
if (product.total === 0) {
calcTotal();
}
useEffect(() => {
setTotal(product.id, product.total);
}, [product.total]);
useEffect(() => {
setMaterial(product.id, product.material);
//setTotal(product.id, product.material + labor);
}, [product.material]);
function ProductMaterial() {
const [name, setName] = useState("");
function handleChange(e) {
setName(parseFloat(e.target.value));
setMaterialState(parseFloat(e.target.value));
setMaterial(product.id, product.material);
calcTotal();
setTotal(product.id, product.total);
}
return (
<input
type="number"
name="firstName"
onChange={handleChange}
value={product.material}
/>
);
}
return (
<tr key={product.name}>
<td>
<ProductMaterial product={product} />{" "}
<td id={`total-${product.id}`}>{product.total}</td>
</tr>
);
}
export default Products;
I am trying to use update state in a react function component but it is not rerendering right away.
My application takes an input for material cost of each product and calculates the total of that product. I would like the component to be rerendered to show the updated total after the input is updated. It seems that the total is calculated and the state is updating every time the material cost is changed, but I can't get the displayed total to say the new amount.
Here's a screenshot of my application (some code for elements in the table were removed for simplicity)
This is what the data looks like. In this example I am saving the first element of the array.
export let data =
{
name: "Name",
description:
"",
products: [
{
id: 1,
name: "Name 1",
material: 1.05,
time: 25,
total: 0,
},
{
id: 2,
name: "Name 2",
material: 3,
time: 252,
total: 0,
},
],
}
This is the parent component. It has the main render and the setTotal and setMaterial functions. The render maps through each of the productData.products so the child component can render them.
The setTotal and setMaterial functions are almost identical (I'm sure there's a way to refactor that). This code seems to work well to update state, although I do notice that doing console.log() within those functions isn't working.
function CompareCard({ type, labor }) {
const [productData, setProductData] = useState(data);
const setTotal = () => {
function setTotalUpdate(id, total) {
const productPrevious = productData.products.find(function (rec) {
return rec.id === id;
});
const productUpdated = {
...productPrevious,
total: total,
};
const productNew = productData.products.map(function (rec) {
return rec.id === id ? productUpdated : rec;
});
const productFullNew = {
...productData,
products: productNew,
};
setProductData(productFullNew);
}
};
const setMaterial = () => {
function setMaterialUpdate(id, materialCost) {
const productPrevious = productData.products.find(function (rec) {
return rec.id === id;
});
const productUpdated = {
...productPrevious,
material: materialCost,
};
const productNew = productData.products.map(function (rec) {
return rec.id === id ? productUpdated : rec;
});
console.log("productsNew:", productNew);
const productFullNew = {
...productData,
products: productNew,
};
setProductData(productFullNew);
}
};
return (
<div className="Card">
<h3> {productData.name} </h3>
<p>{productData.description}</p>
<table className="table">
<thead>
<tr>
<th scope="col">
<p>
<b> Material </b>
</p>
</th>
<th scope="col">
<p>
<b> Labor </b>
</p>
</th>
<th scope="col">
<p>
<b> Total</b>
</p>
</th>
</tr>
</thead>
<tbody id={`${productData.name}`}>
{productData.products.map((product) => {
return (
<Products
key={product.id}
product={product}
labor={3}
setMaterial={setMaterial}
setTotal={setTotal}
/>
);
})}
</tbody>
</table>
</div>
);
}
export default CompareCard;
This is the child element. It handles each product individually.
The first calcTotal() is used to set the original total. This call works, but the total doesn't continue to rerender when the material is changed later.
import React, { useState, useEffect } from "react";
function Products({ product, labor, setMaterial, setTotal }) {
function setMaterialState(newMaterial) {
product.material = newMaterial;
}
function calcTotal() {
product.total = labor + product.material;
}
if (product.total === 0) {
calcTotal();
}
useEffect(() => {
setTotal(product.id, product.total);
}, [product.total]);
useEffect(() => {
setMaterial(product.id, product.material);
//setTotal(product.id, product.material + labor);
}, [product.material]);
function ProductMaterial() {
const [name, setName] = useState("");
function handleChange(e) {
setName(parseFloat(e.target.value));
setMaterialState(parseFloat(e.target.value));
setMaterial(product.id, product.material);
calcTotal();
setTotal(product.id, product.total);
}
return (
<input
type="number"
name="firstName"
onChange={handleChange}
value={product.material}
/>
);
}
return (
<tr key={product.name}>
<td>
<ProductMaterial product={product} />{" "}
<td id={`total-${product.id}`}>{product.total}</td>
</tr>
);
}
export default Products;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
它不起作用,因为 setMaterial 和 setTotal 都有一个内部函数,但内部函数永远不会执行。
您应该删除函数 setTotalUpdate 和 setMaterialUpdate 来解决更新问题,如下所示。
It doesn't work because both setMaterial and setTotal have an inner function , but the inner function is never executed.
You should remove the functions setTotalUpdate and setMaterialUpdate to resolve your update problem, as below.