网格视图未按空格,行和列显示,当我单击删除菜单项时,它会将最后一个数组值(最后一张卡片的值)传递给函数,而不是单击的卡片的值。在“网格”视图中出了点问题。
以下是卡所使用的数据。有导入语句。
数组:
0: {id: "5", title: "Java", price: "78$"}
1: {id: "2", title: "C++", price: "79$"}
2: {id: "4", title: "C", price: "127$"}
3: {id: "1", title: ".Net", price: "65$"}
4: {id: "3", title: "React Js", price: "67$"}
这是我组件的代码:
const styles = theme => ({
root: {
flexGrow: 1,
},
paper: {
padding: theme.spacing.unit * 2,
textAlign: 'center',
color: theme.palette.text.secondary,
},
card: {
maxWidth: 400,
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9
},
actions: {
display: 'flex',
},
});
const ITEM_HEIGHT = 40;
class Products extends Component {
constructor() {
super();
this.state = {
products: [],
searchString: ''
};
this.getProducts()
}
state = {
anchorEl: null,
};
handleClick = event => {
this.setState({ anchorEl: event.currentTarget });
};
handleClose = () => {
this.setState({ anchorEl: null });
};
delete = id => {
alert(id)
axios.post('http://localhost:9022/products/delete/' + id)
.then(res => {
let updatedProducts = [...this.state.products].filter(i => i.id !== id);
this.setState({ products: updatedProducts });
});
}
getProducts() {
axios.get('http://localhost:9022/products/getAll')
.then(res => {
this.setState({ products: res.data });
console.log(this.state.products);
});
}
onSearchInputChange = (event) => {
if (event.target.value) {
this.setState({ searchString: event.target.value })
} else {
this.setState({ searchString: '' })
}
this.getProducts()
}
render() {
const { anchorEl } = this.state;
const open = Boolean(anchorEl);
const { classes } = this.props;
return (
<div>
<TextField style={{ padding: 24 }}
id="searchInput"
placeholder="Search for products"
margin="normal"
onChange={this.onSearchInputChange} />
<Grid container spacing={12}>
<Grid item xs={4} xm={4}>
<div className="row">
{this.state.products.map(currentProduct => (
<div key={currentProduct.id}>
<Card>
<CardHeader
action={
<IconButton aria-label="More"
aria-owns={open ? 'long-menu' : null}
aria-haspopup="true"
onClick={this.handleClick}>
<MoreVertIcon />
<Menu
id="long-menu"
anchorEl={anchorEl}
open={open}
onClose={this.handleClose}
PaperProps={{
style: {
maxHeight: ITEM_HEIGHT * 4.5,
width: 100,
},
}}
>
<MenuItem component={Link} to={'/products/' + currentProduct.id}>Edit
</MenuItem>
<MenuItem onClick={() => this.delete(currentProduct.id)}>Delete
</MenuItem>
</Menu>
</IconButton>
}
title={currentProduct.title}
/>
<CardContent>
<Typography component="p">
{currentProduct.id}
</Typography>
</CardContent>
</Card>
</div>
))}
</div>
</Grid>
</Grid>
</div>
)
}
}
export default withStyles(styles)(Products);
答案 0 :(得分:0)
我知道问题出在哪里。您的代码逻辑有问题。
您在action
的{{1}}部分中尝试做的是渲染其中包含两个静态项目的CardHeader
。
Menu
问题是<MenuItem component={Link} to={'/products/' + currentProduct.id}>Edit</MenuItem> <MenuItem onClick={() => this.delete(currentProduct.id)}>Delete</MenuItem>
必须具有唯一的ID,但是每次渲染它时,您都必须使用相同的=> Menu
,而可以执行类似simple-menu
的操作。最好的办法是从simple-menu-${currentProduct.id}
而不是CardHeader
渲染一个单独的组件。
这使您可以更好地控制组件和要渲染的每个元素。
我个人不喜欢在卡片中放置actions
,而是将Menu
放置在卡片的左上/右上。
取消对
icons
中的action属性的注释,并注释掉第一个组件,以了解我的意思!
我希望这很清楚,如果不是,请告诉我!
答案 1 :(得分:0)
您可以解决一些问题,这些问题应该可以解决所有问题,或者至少可以指导您正确的方向。
我将指导您完成操作,但是如果您read the docs first,这将是理想的选择。
this.getProducts()
,该函数使用setState
anchorEl
并将其设置为构造函数之外的状态super
,这可能会导致错误this
的函数(handleClick,handleClose,getProducts等),这可能导致undefined
的{{1}}状态。this
之后立即从状态获取值的,由于setState
在React中的工作方式,这些函数可能无法获取正确的值。您应避免全部使用。
构造函数
Constructor来自官方文档:
您不应在构造函数()中调用setState()。相反,如果您的 组件需要使用本地状态,将初始状态分配给 直接在构造函数中使用this.state:
React组件的构造函数在挂载之前被调用。 在实现React.Component子类的构造函数时,您可以 应该在其他任何语句之前调用super(props)。除此以外, this.props将在构造函数中未定义,这可能导致 错误。
通常,在React中,构造函数仅用于两个目的:
- 通过将对象分配给this.state来初始化本地状态。
- 将事件处理程序方法绑定到实例。
您的代码:
setState
将其更改为:
constructor() {
super();
this.state = {
products: [],
searchString: ''
};
this.getProducts()
}
state = {
anchorEl: null,
};
首次获取
要在应用启动时调用constructor(props) {
super(props);
this.state = {
products: [],
searchString: '',
anchorEl: null,
};
this.onSearchInputChange = this.onSearchInputChange .bind(this);
this.getProducts = this.getProducts.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleClose = this.handleClose.bind(this);
}
,请不要使用构造函数,而应使用this.getProducts()
。
componentDidMount来自官方文档:
componentDidMount()在组件被调用后立即被调用 安装(插入树中)。需要DOM的初始化 节点应该去这里。如果您需要从远程端点加载数据, 这是实例化网络请求的好地方。
在组件内部创建此函数:
componentDidMount
绑定
Binding来自官方文档:
有几种方法可以确保函数可以访问组件 属性,例如this.props和this.state,取决于语法 并建立您正在使用的步骤 -绑定到构造函数(ES2015) -类属性(第3阶段提案) -绑定到渲染
您可以使用其中任何一个,但我建议您使用第一个。
您的功能:
componentDidMount(){
this.getProducts();
}
将它们更改为:
handleClick = event => {
this.setState({ anchorEl: event.currentTarget });
};
handleClose = () => {
this.setState({ anchorEl: null });
};
正确使用setState
setState来自官方文档
handleClick(event) { this.setState({ anchorEl: event.currentTarget }); }; handleClose() { this.setState({ anchorEl: null }); };
setState()使更改进入组件状态并告知React 该组件及其子组件需要使用 更新状态。这是您用来更新用户的主要方法 界面以响应事件处理程序和服务器响应。
setState()并不总是立即更新组件。它可能 批处理或将更新推迟到以后。这使得阅读this.state 在调用setState()之后立即发生潜在的陷阱。相反,使用 componentDidUpdate或setState回调(setState(updater, 回调)),保证在更新后都会触发 已应用。如果您需要根据之前的状态设置状态 状态,请阅读下面的updater参数。
setState(updater[, callback])
所以您不应该这样做:
this.setState((state, props) => {
return {counter: state.counter + props.step};
});
因为不能保证在调用onSearchInputChange = (event) => {
if (event.target.value) {
this.setState({ searchString: event.target.value })
} else {
this.setState({ searchString: '' })
}
this.getProducts()
}
时,以前的this.getProducts()
函数已经完成。这意味着它在大多数情况下都可以使用,但是在某些情况下,React尚未完成状态更新,而您已经在调用setState
。
相反,您应该在setState完成后调用this.getProducts()
,并确保像这样使用回调(而且我也在更改函数的声明,因为我们已经在构造函数中将其与之前的更改绑定在一起) :
this.getProducts()
您的onSearchInputChange(event) {
let newSearchString = '';
if (event.target.value) {
newSearchString = event.target.value;
}
// call getProducts once React has finished updating the state using the callback (second argument)
this.setState({ searchString: newSearchString }, () => {
this.getProducts();
});
}
没问题(现在我们将其绑定在构造函数中),但是您在不应该调用getProducts
的情况下正在调用它:
console.log
根据之前对setState的解释,将其命名为:
getProducts() {
axios.get('http://localhost:9022/products/getAll')
.then(res => {
this.setState({ products: res.data });
console.log(this.state.products);
});
}
假设您的数据实际上是一个数组,如下所示:
getProducts() {
axios.get('http://localhost:9022/products/getAll')
.then(res => {
this.setState({ products: res.data }, () => {
console.log(this.state.products);
});
});
}
您拥有的代码应与组件中的先前更改一起使用。但是,您也可以改进某些地方。
这是您的代码:
products: [
{id: "5", title: "Java", price: "78$"}
{id: "2", title: "C++", price: "79$"}
{id: "4", title: "C", price: "127$"}
{id: "1", title: ".Net", price: "65$"}
{id: "3", title: "React Js", price: "67$"}
]
我将参考setState文档的文档,其中解释了更新程序功能:
delete = id => { alert(id) axios.post('http://localhost:9022/products/delete/' + id) .then(res => { let updatedProducts = [...this.state.products].filter(i => i.id !== id); this.setState({ products: updatedProducts }); }); }
第一个参数是带有签名的更新程序函数
setState(updater[, callback])
state是对更改时组件状态的引用 正在应用。它不应该直接突变。相反,变化 应该通过根据来自 状态和道具。例如,假设我们要增加一个值 处于道具状态。
(state, props) => stateChange
更新器功能接收到的状态和道具 保证是最新的。更新器的输出是 与国家浅层融合。
了解何时使用此更新程序功能以及该功能中的this.setState((state, props) => {
return {counter: state.counter + props.step};
});
参数非常重要。
最简单的情况是他们提到的情况:
state
可以这样做:
this.setState((state, props) => {
return {counter: state.counter + props.step};
});
但是,由于您不能保证,this.setState({counter: this.state.counter + this.props.step});
已经成功并且已经完成了值的更新,因此您应该使用updater函数。
现在,返回您的删除功能。
更改此:
setState
(请注意,我已在更新程序功能中将参数名称delete = id => {
alert(id)
axios.post('http://localhost:9022/products/delete/' + id)
.then(res => {
let updatedProducts = [...this.state.products].filter(i => i.id !== id);
this.setState({ products: updatedProducts });
});
}
更改为state
,以使其更有意义且更易于理解):
prevState
请务必注意,在setState之前进行如下过滤:
delete = id => {
alert(id);
axios.post('http://localhost:9022/products/delete/' + id)
.then(res => {
// To guarantee you get the correct values, get them from the state in the updater function in setState
this.setState((prevState, prevProps) => {
// This happens inside the setState function
let updatedProducts = [...prevState.products].filter(i => i.id !== id);
// The updater function must return the values that will be modified in the state
return ({
products: updatedProducts
});
});
});
}
大多数情况下都会工作,但不建议这样做,在处理这种情况时请使用updater函数,以确保每次都能正常工作。