如何更新对象数组中的对象
2022-05-23
658
我有以下部分 React 代码: 这是用于将新对象项添加到数组的处理程序。
因此,我正在检查对象是否存在于整个对象数组中。如果是,我想将此对象的数量增加 1。否则,我想添加对象并将数量设置为 1。
当我想添加第二个相同的产品(相同的 ID)时,我收到错误
Uncaught TypeError: Cannot assign to read only property 'quantity' of object '#<Object>'
export const Component = ({ book }: { book: Book }) => {
const [basket, setBasket] = useRecoilState(Basket);
const handleAddBookToBasket = () => {
const find = basket.findIndex((product: any) => product.id === book.id)
if (find !== -1) {
setBasket((basket: any) => [...basket, basket[find].quantity = basket[find].quantity + 1])
} else {
setBasket((basket: any) => [...basket, { ...book, quantity: 1 }])
}
}
编辑:
if (find !== -1) {
setBasket((basket: any) =>
basket.map((product: any) => ({
...product,
quantity: product.quantity + 1,
}))
);
} else {
setBasket((basket: any) => [...basket, { ...book, quantity: 1 }]);
}
2个回答
数据结构
我认为您的“问题”的根源在于您为购物篮选择了错误的数据结构。使用
Array<Item>
意味着每次您需要更新特定商品时,您都必须执行
O(n)
线性搜索。
如果将购物篮更改为
{ [ItemId]: Item } ,则可以执行即时
O(1)
更新。请参阅下面的完整示例。单击某些产品以将其添加到您的购物篮中。在输出中检查更新后的购物篮数量。
function App({ products = [] }) {
const [basket, setBasket] = React.useState({})
function addToBasket(product) {
return event => setBasket({
...basket,
[product.id]: {
...product,
quantity: basket[product.id] == null
? 1
: basket[product.id].quantity + 1
}
})
}
return <div>
{products.map((p, key) =>
<button key={key} onClick={addToBasket(p)} children={p.name} />
)}
<pre>{JSON.stringify(basket, null, 2)}</pre>
</div>
}
const products = [
{ id: 1, name: "ginger" },
{ id: 2, name: "garlic" },
{ id: 3, name: "turmeric" }
]
ReactDOM.render(<App products={products}/>, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
对象更新
作为一种良好做法,您可以创建一个用于不可变对象更新的函数。
// Obj.js
const update = (o, key, func) =>
({ ...o, [key]: func(o[key]) })
export { update }
然后在需要此行为的地方将其
import
。请注意,也可以在嵌套对象更新中使用。
// App.js
import * as Obj from "./Obj"
function App({ products = [] }) {
// ...
function addToBasket(product) {
return event => setBasket(
Obj.update(basket, product.id, (p = product) => // ✅
Obj.update(p, "quantity", (n = 0) => n + 1) // ✅
)
)
}
// ...
}
对象移除
您可以使用类似的技术从购物篮中
移除
一个项目。不要将行为直接耦合到需要移除的组件中,而是将其添加到
Obj
模块中。
// Obj.js
const update = (o, key, func) =>
({ ...o, [key]: func(o[key]) })
const remove: (o, key) => { // ✅
const r = { ...o }
delete r[key]
return r
}
export { update, remove } // ✅
现在,您可以在任何需要它的组件中
导入
移除
行为。
function App() {
const [basket, setBasket] = React.useState({
1: { id: 1, name: "ginger", quantity: 5 },
2: { id: 2, name: "garlic", quantity: 6 },
3: { id: 3, name: "tumeric", quantity: 7 },
})
function removeFromBasket(product) {
return event => {
setBasket(
Obj.remove(basket, product.id) // ✅
)
}
}
return <div>
{Object.values(basket).map((p, key) =>
<div key={key}>
{p.name} ({p.quantity})
<button onClick={removeFromBasket(p)} children="❌" />
</div>
)}
<pre>{JSON.stringify(basket, null, 2)}</pre>
</div>
}
// inline module for demo
const Obj = {
remove: (o, key) => {
const r = {...o}
delete r[key]
return r
}
}
ReactDOM.render(<App />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
よつば
2022-05-23
在您的
setBasket
中,您应该创建一个新对象,而不是更新当前对象
您的代码应该看起来像这些:
{...basket, ...{
quantity : basket.quantity + 1
}}
OrcusZ
2022-05-23