我正在构建一个非常有缺陷的电子商务应用程序,使用React只是为了好玩,我试图找出如何在某个对象被推入阵列后设置状态。
我有一个$('#option1, #option2, #option3').on('click', function() {
$('#contact_form').bootstrapValidator('enableFieldValidators', 'Truck Options', false);
});
$('#option4').on('click', function() {
$('#contact_form').bootstrapValidator('enableFieldValidators', 'Truck Options', true);
});
数组,我从我最初的cart
数组中添加了我保留所有产品的项目。
不确定的是,我有产品库存。所以,让我们说我的items
产品有5个库存,每当我推送chocolate
对象时,它就会堆积并在购物车中添加相同的商品:
我想将chocolate
对象推送到购物车阵列,但如果已经存在,我不想要复制。相反,我希望实现添加chocolates
对象的内容,但每次添加时它的数量都会相应更改。它看起来像这样:
我怎样才能实现这样的目标?也许检查一下这个对象是否已经添加到chocolate
数组中,然后是不是渲染副本,只需按下值并更新该项的数量?
被困了几个小时,非常感谢一些提示。
cart
答案 0 :(得分:3)
通过ID将您的产品存储在常规对象中。
1: {
id: 1,
name: 'chocolate'
}
将您的购物车存储为一系列ID。
[1, 1, 1]
在您的组件中,按ID分组ID购物车数组以获取计数,并按ID查找购物车对象以获取其数据。
应计算计算数据,而不是存储。
这里有一些完全未经测试的,无条件的代码,显示了在渲染函数中完成的计算:
class App extends Component {
state = {
cart: [],
items: [
{ id: uuid(), name: 'chocolate', price: 10, available: 5 },
{ id: uuid(), name: 'strawberry', price: 50, available: 10 },
{ id: uuid(), name: 'banana', price: 43, available: 20 }
// Convert to an object of { id: { id, name, price } }
].reduce((memo, item) => ({
...memo,
[item.id]: item
}), {}),
}
addItem = id => {
const { cart, } = this.state
this.setState({
cart: [ ...cart, id ]
})
}
removeItem = removeId => {
const { cart, } = this.state
this.setState({
cart: cart.filter(({ id }) => id !== removeId)
})
}
render() {
const { cart, items, total, addToCartMessage, removeFromCartMessage } = this.state
// Given an array of item IDs in our cart, group them into an object
// with the total count and price for each item, and overall count
const accumulatedItems = items.reduce((memo, item) => {
const { id, price } = item;
const { count, price, } = memo[id] || {};
return {
...memo,
cartTotal: memo.cartTotal + price,
[id]: {
count: (count || 0) + 1,
total: (price || 0) + price,
}
};
// Starting object to reduce
}, {
cartTotal: 0,
});
return (
<div className="App">
{cart.length === 0 ? <h3>No items in cart</h3> : (
<div>
<h1>Cart:</h1>
{Object.keys(accumulatedItems).sort().map(id => (
<div key={id}>
<h1>{items[id].name} x {accumulatedItems[id].total}</h1>
<p>${accumulatedItems[id].total}</p>
<button onClick={() => this.removeItem(id)}>Remove From Cart</button>
</div>
))}
</div>
)}
</div>
);
}
}
处理计算数据,并像remaining
一样改变状态,为您的应用增加了显着的逻辑复杂性。一旦出现性能问题,您应该开始担心尝试存储/缓存/记忆计算状态。计算渲染函数中的总计是第一次通过。
在React中,有一些像reselect这样的解决方案,在技术上不需要redux。它们只是缓存处于给定状态的函数,并生成计算输出,并且只有在输入发生变化时才重新计算输出。
答案 1 :(得分:1)
也许你可以在你的问题中思考和解释。有多种方法可以做到这一点,每个人都喜欢这样做,或者他们多么容易做到和维护它。
示例:
cart: [
{ id: uniqeId, quantiy: 1 },
{ id: uniqeId, quantiy: 6 },
]
将商品添加到卡片后,您可以直接更改相关对象的数量。但为此,您必须在此数组中找到该项目,然后根据您的猜测更改数量。
像:
cart: {
ids: [ uniqueId, uniqueId ],
quantity: { uniqueId: 1, uniqueId: 6 }
}
但我也是一个学习者,也许有更好的方法除了那些。我还没有写过购物车,但如果我这样做了,我可能会使用第二种方法。
顺便说一下,不要用push来改变你的状态。这会改变你的状态,这是不可取的。使用类似concat
或扩展运算符的内容来创建新状态。
item = { foo: bar, fizz: buzz }
// now setting state
cart: [ ...this.state.cart, item ]
如果状态更改取决于先前的状态,请尝试在setState中使用回调函数(因为它是异步操作)。
this.setState( prevState => ( {
cart: [ ...prevState.cart, item ],
} ) )
答案 2 :(得分:0)
使用@devserkan重建购物车状态的建议和@Andy Ray关于重组商品状态的建议,我设置我的状态看起来像这样:
state = {
items: {
1: {
id: 1, name: 'chocolate', price: 10, available: 5
},
2: {
id: 2, name: 'strawberry', price: 10, available: 10
},
3: {
id: 3, name: 'banana', price: 10, available: 20
}
}
cart: {
ids: [],
quantity: { 1: 0, 2: 0, 3: 0 }
}
}
然后我去渲染了这些项目并添加了一个onClick函数(addItem
)来处理一些setState调用:
render() {
const { cart, items } = this.state
return (
<div>
<h1>Shopping Area</h1>
{Object.values(items).map(item => (
<div key={item.id}>
<h2>{item.name}</h2>
<h2>{item.price}</h2>
<button onClick={() => this.addItem(item)}>Add To Cart</button>
</div>
))}
在我的addItem
函数中,我继续设置状态cart
,以便我按下商品ID,并更新该ID的数量:
addItem = item => {
const { cart, items } = this.state
this.setState({
cart: {
...cart,
// Push item id to ids array inside cart state
ids: [...cart.ids, item.id],
quantity: {
...cart.quantity,
// Update quantity of the specific id pushed by 1
[item.id]: cart.quantity[item.id] + 1
}
}
})
}
最后我必须渲染购物车部分:我这样做是通过检查cart.ids
数组是否为空并进行另一次检查以仅渲染数量大于0的项目。如果我们没有进行检查,每次我们推送一个项目时,它会同时添加所有3个,我们只希望显示该特定项目。
{cart.ids.length !== 0 ? Object.keys(items).map(id => (
<div key={id}>
// Check to see if quantity for that item is > 0
{cart.quantity[id] > 0 && (
<h1>{items[id].name} x {cart.quantity[id]}</h1>
)}
</div>
)) : <h1>No Items In Your Cart</h1>}
完整代码(无价格/剩余)
export default class App extends Component {
state = {
cart: {
ids: [],
quantity: {
1: 0,
2: 0,
3: 0
}
},
items: {
1: {
id: 1, name: 'chocolate', price: 10, available: 5
},
2: {
id: 2, name: 'strawberry', price: 10, available: 10
},
3: {
id: 3, name: 'banana', price: 10, available: 20
}
}
}
addItem = item => {
const { cart, items } = this.state
this.setState({
cart: {
...cart,
ids: [...cart.ids, item.id],
quantity: {
...cart.quantity,
[item.id]: cart.quantity[item.id] + 1
}
}
})
}
removeItem = removeId => {
const { cart } = this.state
this.setState({
cart: cart.filter(({ id }) => id !== removeId)
})
}
render() {
const { cart, items, total, addToCartMessage, removeFromCartMessage } =
this.state
return (
<div className="App">
<h1>Shopping Area</h1>
{Object.values(items).map(item => (
<div key={item.id}>
<h2>{item.name}</h2>
<h2>{item.price}</h2>
<button onClick={() => this.addItem(item)}>Add To Cart</button>
</div>
))}
<hr style={{'marginTop': '200px'}} />
<h1>Cart</h1>
{cart.ids.length !== 0 ? Object.keys(items).map(id => (
<div key={id}>
{cart.quantity[id] > 0 && (
<h1>{items[id].name} x {cart.quantity[id]}</h1>
)}
</div>
)) : <h1>No Items In Your Cart</h1>}
</div>
)
}
}
非常感谢@Andy Ray和@devserkan的建议。