首页 > 解决方案 > 如何在promise中的componentDidMount中设置状态而不会出现setState错误

问题描述

我正在调用 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 的样板。

标签: javascriptreactjspromisefetch

解决方案


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

componentDidMount() { 
  this.isMounted = true;
}

componentWillUnmount() {
   this.isMounted = false;
}

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

检查包含<ItemList/>. 如果您<ItemList/>基于某些条件进行渲染,则问题可能出在这些条件下。


推荐阅读