【问题标题】:how to setState in componentDidMount in promise without getting setState error如何在promise中的componentDidMount中设置状态而不会出现setState错误
【发布时间】:2018-12-20 05:11:23
【问题描述】:

我正在调用 api 并使用响应设置状态。 我无法在没有看到此错误的情况下调用 setState: 如果 setState 发生在承诺之外,则不会发生错误。

如何在不看到此错误的情况下将 API 响应设置为状态?

无法在未安装的组件上调用 setState(或 forceUpdate)

componentDidMount() {
    const url = 'https://localhost:8000/api/items'
    let items;
    fetch(url, {mode: 'cors'})
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            isLoaded: true,
            items: result
          });
        },
        (error) => {
          this.setState({
            isLoaded: true,
            error
          });
        }
      )
  }

整个组件供参考:

import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import GridList from '@material-ui/core/GridList';
import GridListTile from '@material-ui/core/GridListTile';
import GridListTileBar from '@material-ui/core/GridListTileBar';
import ListSubheader from '@material-ui/core/ListSubheader';
import IconButton from '@material-ui/core/IconButton';
import InfoIcon from '@material-ui/icons/Info';

const styles = theme => ({
  root: {
    display: 'flex',
    flexWrap: 'wrap',
    justifyContent: 'space-around',
    overflow: 'hidden',
    backgroundColor: theme.palette.background.paper,
  },
  icon: {
    color: 'rgba(255, 255, 255, 0.54)',
  },
});

class ItemList extends Component {
  constructor(props) {
    super(props)
    this.state = {
      items: [],
      isLoaded: false,
    }

    this.handleItemClick = this.handleItemClick.bind(this);
  }

  componentDidMount() {
    const url = 'https://localhost:8000/api/items'
    let items;
    fetch(url, {mode: 'cors'})
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            isLoaded: true,
            items: result
          });
        },
        (error) => {
          this.setState({
            isLoaded: true,
            error
          });
        }
      )
  }

  handleItemClick(id) {

  }

  handleRenderItems() {
      const { classes } = this.props
      const { items } = this.state;
      return this.state.items.map((item, idx) => {
      const id = item.id;
      return (
        <GridListTile onClick={() => this.handleItemClick(id)} key={idx}>
        <img src={item.key_image} alt={item.title} />
        <GridListTileBar
          title={item.title}
          subtitle={<span>${item.rent_price_day}/day</span>}
          actionIcon={
            <IconButton className={classes.icon}>
              <InfoIcon />
            </IconButton>
          }
        />
        </GridListTile>
      )
    })
  }

  render() {
    const { classes } = this.props;
    return (
      <div className={classes.root}>
        <GridList cols={3} cellHeight={220} className={classes.gridList}>
          <GridListTile key="Subheader" cols={3} style={{ height: 'auto' }}>
            <ListSubheader component="div">Popular Items</ListSubheader>
          </GridListTile>
          {this.handleRenderItems()}
        </GridList>
      </div>
    );
  }
}

ItemList.propTypes = {
  classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(ItemList);

而不是在组件中处理这个 - 我认为在动作创建器中处理对 API 的调用可能更简单,并使用 redux 来存储数据 - 然后在可用时将其发送到组件。无论如何,最好不要看到这些细节。

此外,通常也不会在视图中使用样式对象 - 但它是带有 material-ui 的样板。

【问题讨论】:

  • 在 componentWillUnmount 中添加一个日志行,并在收到此错误之前检查该日志是否打印。
  • 能贴出组件代码吗?
  • 确实如此。你能从中得出什么结论?
  • @AdamAzad 我正在使用material-ui,所以有相当多的样板,但我会发布它,因为你已经问过了。
  • 这意味着您的组件当时不在那里设置状态,这些承诺得到解决。是什么让父组件卸载这个组件?

标签: javascript reactjs promise fetch


【解决方案1】:

在您的组件中添加以下内容,

componentDidMount() { 
  this.isMounted = true;
}

componentWillUnmount() {
   this.isMounted = false;
}

只有当 this.isMounted 为真时才设置状态。这应该可以解决您的问题,但这不是推荐的模式。请参考https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html

检查包含&lt;ItemList/&gt; 的父组件的代码。如果您基于某些条件渲染&lt;ItemList/&gt;,则问题可能出在这些条件下。

【讨论】:

  • 我要提到你做了什么,它在某种程度上是一种反模式。它可能有效,但并不理想。
  • 您能否发布包含可能有助于确定问题的 的父组件的代码。
【解决方案2】:

很简单。您只需在变量中返回第二个承诺的值。 像这样:

  let items; 
  let errorFetch;   
  fetch(url, {mode: 'cors'})
  .then(res => res.json())
  .then(
    (result) => {
      items = result;
    },
    (error) => {
      errorFetch = error
    }
  );
  this.setState(items ? { isLoaded : true, items } : { isLoaded : true, error: errorFetch });

【讨论】:

  • 这里有一个小问题,即设置状态时可用的值。因为这个 - items === undefined 而不是来自 API 的值,所以这个方法很遗憾不起作用。使用 setTimeout 可能有效,但我认为没有必要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-19
  • 1970-01-01
  • 2018-10-02
  • 1970-01-01
  • 2016-06-21
相关资源
最近更新 更多