Render() 状态元素最初为空,直到 api 调用

Render() state element is initially empty until api call

我有一个 class 组件,它应该在 API 调用后显示一些列表值,在我的渲染函数中,我调用一个函数来填充一些其他状态列表(具有来自获取的列表),问题是在渲染调用中,状态值最初是空的,因此组件 I return 也只是空的。

我试过使用 componentDidUpdate() 但我不太清楚如何使用它,它通常会给我一个无限循环。

这是我的相关代码:


class AdminSales extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      items: [],
      data: [],
    };
  }

  componentDidMount() {
    this.fetchData();
  }

  fetchData() {
    fetch("/api/items")
      .then((res) => res.json())
      .then((items) => this.setState({ items: items }));
  }

  componentDidUpdate(prevState) {
    if (JSON.stringify(prevState.items) == JSON.stringify(this.state.items)) {
      // Do nothing 
    } else {
      // This gives infinite loop ...
      // this.fetchData();
    }

  }

  populateData() {
    this.state.items.forEach(function (item) {
      this.state.data.push({
        name: item.name,
        value: item.quantity,
      });
    }, this);
  }

  render() {
    // Output shown line: 65
    console.log(this.state);
    this.populateData();
    const { data } = this.state;
    return ( ... );
  }
}

export default AdminSales;

任何帮助将不胜感激。

首先你的代码中存在多个问题

  • Updating/Mutating 状态 render
  • 不是使用 setState 更新状态,而是在 populateData 方法中更新状态

此外,正如@Drew 提到的,我们不必将 items 复制到 data 中,而是在从 API.

在等待响应的同时,如果您想显示加载信息,您也可以这样做。

以下是涵盖上述所有要点的示例。

const mockAPI = () => {
  return new Promise((resolve) => setTimeout(() => {
    resolve([{id:1, name: "ABC", quantity: 1}, {id: 2, name: "DEF", quantity: 5}, {id: 3, name: "XYZ", quantity: 9}])
  }, 500));
}

class AdminSales extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      items: [],
      loading: true
    };
  }

  componentDidMount() {
    this.fetchData();
  }

  fetchData = () => {
    mockAPI()
      .then((res) => {
        this.populateData(res);
      });
  }

  populateData = (data) => {
    this.setState({ 
      items: data.map(({name, quantity}) => ({
        name,
        value: quantity
      })),
      loading: false
    })
  }

  render() {
    //console.log(this.state);
    const { items, loading } = this.state;
    return loading ? <p>Loading...</p> : 
    items.map(item => (
      <div>{item.name}: {item.value}</div>
    ));
  }
}


ReactDOM.render(<AdminSales />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="react"></div>

Note: For simplicity I've mocked the backend API with simple setTimeout.