我是一个反应型菜鸟,我需要我正在使用的购物车的帮助。
我正在尝试获取一个按钮,以从我拥有的对象数组中删除一个项目,但是不知道如何从树中的位置访问它。
我映射到cartItems变量上,并使它们显示在页面上,但是现在有将其传递给另一个组件的情况。
请保持温柔,这是我的第一篇文章。
这是应用程序组件中的代码:
const cartItems =
[
{
id: 1,
image: image1,
description: 'Cotton Tshirt',
style: 'MS13KT1906',
color: 'blue',
size: 'S',
price: 11.00,
button: <RemoveButton />
},
{
image: image3,
description: 'Print Girls Tee',
style: 'MS13KT1906',
color: 'pink',
size: 'S',
price: 17.00,
button: <RemoveButton />
},
{
image: image2,
description: 'Flower Pattern Shirt',
style: 'MS13KT1906',
color: 'blue',
size: 'S',
price: 9.00,
button: <RemoveButton />
},
{
image: image4,
description: 'Check Pattern Shirt',
style: 'MS13KT1906',
color: 'red',
size: 'M',
price: 22.00,
button: <RemoveButton />
}
];
return (
<div>
<ItemList
cartItems={cartItems.map((item, i) =>
<ul key={i}>
<li>
<img src={item.image} />
</li>
<li>
Description: {item.description}
</li>
<li>
Color: {item.color}
</li>
<li>
Size: {item.size}
</li>
<li>
Price: {item.price}
</li>
<li>
{item.button}
</li>
</ul>
)} />
<RemoveButton />
</div>
);
}
}
以及“删除按钮组件”中的代码
onClickRemove(e, item) {
console.log(e) <-- what do i do here?
}
render() {
return(
<div>
<button onClick={this.onClickRemove}>
X Remove
</button>
</div>
)
}
}
答案 0 :(得分:0)
对数组中的每个对象都具有唯一键,就像零索引对象一样。
将对象数组保持在该状态,以便您可以删除对象并再次执行setState以查看更新的数据。
您应该将事件处理函数保留在迭代数据的位置,并将事件处理函数作为道具传递给RemoveButton。这将用作reactjs中的回调。
我更喜欢以下实现此类事情的方式
class CartItems extends Component{
constructor(props){
super(props);
this.state = {
cartItems: []
}
}
componentWillMount(){
const cartItems =
[
{
id: 1,
image: image1,
description: 'Cotton Tshirt',
style: 'MS13KT1906',
color: 'blue',
size: 'S',
price: 11.00,
button: <RemoveButton />
},
{
id: 2,
image: image3,
description: 'Print Girls Tee',
style: 'MS13KT1906',
color: 'pink',
size: 'S',
price: 17.00,
button: <RemoveButton />
},
{
id: 3,
image: image2,
description: 'Flower Pattern Shirt',
style: 'MS13KT1906',
color: 'blue',
size: 'S',
price: 9.00,
button: <RemoveButton />
},
{
id: 4,
image: image4,
description: 'Check Pattern Shirt',
style: 'MS13KT1906',
color: 'red',
size: 'M',
price: 22.00,
button: <RemoveButton />
}
];
this.setState({
cartItems: cartItems
});
}
onClickRemove(id, image) {
let items = this.state.cartItems;
items = items.filter((item) => {
return item.id !== id;
});
this.setState({
cartItems: items
});
}
render(){
return (
<div>
<ItemList cartItems={this.state.cartItems.length> 0 && this.state.cartItems.map((item, i) =>
<ul key={i}>
<li>
<img src={item.image} />
</li>
<li>
Description: {item.description}
</li>
<li>
Color: {item.color}
</li>
<li>
Size: {item.size}
</li>
<li>
Price: {item.price}
</li>
<li>
{item.button}
</li>
</ul>
)} />
<RemoveButton onClickRemove={this.onClickRemove(item.id, item.image)}/>
</div>
);
}
}
删除按钮组件
class RemoveButton extends Component{
render() {
return(
<div>
<button onClick={this.props.onClickRemove}>
X Remove
</button>
</div>
)
}
}
}
答案 1 :(得分:0)
任何“活着”的变量都需要在某个地方处于状态。这是一个示例购物车应用,可让您保持购物状态。
function App() {
return (
<div className="App">
<Cart />
</div>
);
}
class Cart extends React.Component {
state = {
inCart: [
{
id: 1,
description: "Cotton Tshirt",
style: "MS13KT1906",
color: "blue",
size: "S",
price: 11
},
{
id: 2,
description: "Print Girls Tee",
style: "MS13KT1906",
color: "pink",
size: "S",
price: 17
},
{
id: 9,
description: "Check Pattern Shirt",
style: "MS13KT1906",
color: "red",
size: "M",
price: 22.5
}
]
};
// or just pass the id, and make the check `(product.id !== removedProductId)`
removeCallback = removedProduct => {
this.setState(state => ({
inCart: state.inCart.filter(product => product.id !== removedProduct.id)
}));
};
render() {
const { inCart } = this.state;
if (!inCart.length) {
return (
<p>Cart is empty.</p>
);
}
return (
<table>
{inCart.map(product => (
<CartRow
key={product.id}
product={product}
removeCallback={this.removeCallback}
/>
))}
</table>
);
}
}
class CartRow extends React.Component {
removeOnClick = removedProduct =>
this.props.removeCallback(this.props.product);
render() {
const { product, removeCallback } = this.props;
return (
<tr>
<td>{product.description}</td>
<td>{product.price}</td>
<td>
<RemoveButton onClick={this.removeOnClick} />
</td>
</tr>
);
}
}
class RemoveButton extends React.Component {
render() {
return <button {...this.props}>Remove</button>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
.App {
font-family: sans-serif;
text-align: center;
}
td {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
要强调的两件事是:
id
)但是,我会强烈鼓励考虑使用avoiding arrays of objects, and flattening your data。除了使用对象数组之外,您还可以使用由ID(以便于访问)和代表这些项目组的ID数组作为键的对象。在完整的应用程序中,您可能有一个名为products
的东西,代表您出售的商品所有。您的购物车就是被添加来购买的那些。因此products
将是我们的id键对象,而inCart
只是id的数组。这是具有该形状的示例购物车应用程序(产品位于最高组件中,因为它可能会在您的整个应用程序中使用):
function App() {
const products = {
1: {
id: 1,
description: "Cotton Tshirt",
style: "MS13KT1906",
color: "blue",
size: "S",
price: 11
},
2: {
id: 2,
description: "Print Girls Tee",
style: "MS13KT1906",
color: "pink",
size: "S",
price: 17
},
4: {
id: 4,
description: "Flower Pattern Shirt",
style: "MS13KT1906",
color: "blue",
size: "S",
price: 9
},
9: {
id: 9,
description: "Check Pattern Shirt",
style: "MS13KT1906",
color: "red",
size: "M",
price: 22.5
}
};
return (
<div className="App">
<Cart products={products} />
</div>
);
}
class Cart extends React.Component {
state = {
inCart: [1, 2, 9]
};
removeCallback = removedId => {
this.setState(state => ({
inCart: state.inCart.filter(productId => productId !== removedId)
}));
};
// for testing only
refillCart = () => {
this.setState(state => ({
inCart: [1, 2, 9]
}));
};
render() {
const { products } = this.props;
const { inCart } = this.state;
if (!inCart.length) {
return (
<p>
Cart is empty. <button onClick={this.refillCart}>Refill?</button>
</p>
);
}
return (
<table>
{inCart.map(productId => (
<CartRow
key={productId}
products={products}
productId={productId}
removeCallback={this.removeCallback}
/>
))}
</table>
);
}
}
class CartRow extends React.Component {
removeOnClick = removedId => this.props.removeCallback(this.props.productId);
render() {
const { products, productId, removeCallback } = this.props;
const product = products[productId];
return (
<tr>
<td>{product.description}</td>
<td>{product.price}</td>
<td>
<RemoveButton onClick={this.removeOnClick} />
</td>
</tr>
);
}
}
class RemoveButton extends React.Component {
render() {
return <button {...this.props}>Remove</button>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
.App {
font-family: sans-serif;
text-align: center;
}
td {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
这样,inCart
简短明了,对其进行修改只需要过滤数组即可。
一个看似“否定”的可能是,您必须不断将products
传递到树上。但是,a)在树上传递东西是React的事,b)非常很有可能随着您应用的增长,您将开始使用某种全局状态库(例如Redux)或React's context API ,然后根据需要将每个组件连接到该全局状态。在那个阶段,您不需要一直向下传递products
,而是可以根据需要将其“导入”到组件中(并且不需要向下传递回调,您可以执行全局操作来修改全局状态)。
答案 2 :(得分:0)
我意识到我的other answer跳过了如何让onClickRemove
知道当您遍历项目时要删除哪个项目的问题。我知道这样做的三种方式:
1。通过传递的参数传递匿名函数(或绑定参数)
<RemoveButton onClick={() => this.onClickRemove(item)}>
// or: <RemoveButton onClick={this.onClickRemove.bind(this, item)}>
这可能带有fairly minor performance hit。
2。使用currying-一个接受您的参数并返回另一个函数的函数
因此,在onClick
中,您实际上是在调用方法:
<RemoveButton onClick={this.onClickRemove(item)}>
该方法是一个接受项目的函数,然后返回另一个处理该项目的函数:
onClickRemove = (item) => () => { /* do something with `item` */ }
这避免了选项1带来的性能下降。但是,老实说,我从来没有在野外看到它;仅作为示例。也许人们不了解或不关心性能下降,或者只是发现此语法不太吸引人,或者只是没有想到它。
3。 (推荐)完全避免这种情况,而是在每个循环上创建一个新组件(该组件作为道具)
React ESLint library recommends就是这样,我们已经完成了:
{inCart.map(productId => (
<CartRow
key={productId}
products={products}
productId={productId}
removeCallback={this.removeCallback}
/>
))}
现在,它是它自己的组件,您的<CartRow />
组件可以使用<RemoveButton onClick={this.removeOnClick} />
方法,来执行removeOnClick
:
removeOnClick = removedId => this.props.removeCallback(this.props.productId);
由于您仅通过了 该产品,因此我们可以使用该方法对要删除的产品进行硬编码。 (可以说,这也是拆分组件的好地方。)
答案 3 :(得分:-1)
这种代码结构效率不高,我强烈建议您在生产中不要实际使用它,但是仍然使用与您设置的相同的体系结构,可以将项目索引传递给组件。
例如
<ItemList
cartItems={cartItems.map((item, i) =>
<ul key={i}>
<li>
<img src={item.image} />
</li>
<li>
Description: {item.description}
</li>
<li>
Color: {item.color}
</li>
<li>
Size: {item.size}
</li>
<li>
Price: {item.price}
</li>
<li>
{item.button}
</li>
</ul>
)} />
<RemoveButton index={i} /> // i = index in the Object Array
</div>
);
RemoveButton组件现在知道与之关联的索引。现在在您的组件中,您可以获取该索引并对其进行处理,例如
onClickRemove(index) {
// do something with index.
}
render() {
return(
<div>
<button onClick={this.onClickRemove.bind(this, this.props.index}>
X Remove
</button>
</div>
)
}
}
最好使用bind
方法,因为lambda表达式可能会带来负面的性能问题。阅读有关绑定here的更多信息以及有关组件和道具here
这有点混乱。最好让父对象处理onClick函数,因为它可以访问当前的ItemList,并且如果不是const,可以对其进行修改。但是我不知道用例,所以这可能是完全有效的!