网格视图显示不正确

时间:2018-10-20 06:08:36

标签: javascript reactjs

网格视图未按空格,行和列显示,当我单击删除菜单项时,它会将最后一个数组值(最后一张卡片的值)传递给函数,而不是单击的卡片的值。在“网格”视图中出了点问题。

以下是卡所使用的数据。有导入语句。

数组:

    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);

2 个答案:

答案 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渲染一个单独的组件。

  

这使您可以更好地控制组件和要渲染的每个元素。

在此处查看和编辑它:
Edit MaterialUI Remove Product

我个人不喜欢在卡片中放置actions,而是将Menu放置在卡片的左上/右上。

  

取消对icons中的action属性的注释,并注释掉第一个组件,以了解我的意思!

我希望这很清楚,如果不是,请告诉我!

答案 1 :(得分:0)

您可以解决一些问题,这些问题应该可以解决所有问题,或者至少可以指导您正确的方向。

我将指导您完成操作,但是如果您read the docs first,这将是理想的选择。

  1. 您在构造函数中调用this.getProducts(),该函数使用setState
  2. 您正在初始化anchorEl并将其设置为构造函数之外的状态
  3. 您在不传递道具的情况下呼叫super,这可能会导致错误
  4. 您不是在绑定使用this的函数(handleClick,handleClose,getProducts等),这可能导致undefined的{​​{1}}状态。
  5. 您正在调用的函数是在调用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

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函数,以确保每次都能正常工作。