ReactJS:多个setStates异步发生,状态未更新

时间:2018-10-13 18:18:42

标签: javascript reactjs

在启动渲染器中的函数(filterAndSort)之前,遇到状态未更新的问题。我在函数内添加了console.log语句,并且只有一种状态被更新。我的代码对我来说似乎合乎逻辑,因为我设置了一个if条件,仅在setState发生后才触发函数

完整错误消息:

  

警告:无法在已卸载的设备上调用setState(或forceUpdate)   零件。这是空操作,但表示您的内存泄漏   应用。要修复,请取消所有订阅和异步任务   在componentWillUnmount方法中。

但是,我怀疑是因为我有多个setState异步发生。

我在想,也许只有在获取所有axios请求之后,才需要将所有变量的componendDidMount重写为setState。 Multiple Axios Requests Into ReactJS State

我认为另一种解决方案是将函数的返回结果存储为状态而不是变量,然后添加componentDidUpdate。

componentDidUpdate(prevProps, prevState) {
  if (this.state.value > prevState.value) {
    this.filterAndSort();  
  }
}

组件

class Results extends Component {

  constructor(){
    super()
    this.state = {
      results: [],
      races: [],
      arr = []
      isLoading: true
    };
  }

  componentDidMount(){

    const oneRequest = axios.get(URL_ONE)
                              .then(response =>
                                    response.data.data.map(result => ({...
                                    ))
                                    )
                              .then(results => this.setState({results, isLoading: false}))    

    const twoRequest = axios.get(URL_TWO)
                              .then(response =>
                                response.data.data.map(race => ({...}))
                                )
                          .then(races => this.setDefault(races))

  }

   setDefault = (races) => {
    ........
    this.setState({arr, races, isLoading:false})
  }

  filterAndSort = (races, results) => {
    console.log(races, results)
    .......
  }

  render() {

    const{races, results} = this.state

      if (isLoading == true) {
        return (
          <div>
            <p>Loading...</p>   
          </div>
        )
      } else {
        return (
          <div>
            <BarChart 
              qualData={this.filterAndSort(races, results)} 
              raceData={this.filterAndSort(races, results)} 
              width="1200" 
              height="500" />

          </div>
          );
        }
      }

}

export default Results;

2 个答案:

答案 0 :(得分:0)

好吧,我想到了很多事情。

首先,仅当您拥有两个数据中的一个(以先到者为准)时,才将isLoading设置为false,因此render方法有时会使用空的小数或结果来调用函数。 另一件事,您正在异步调用setState。在请求完成时,您的组件可能不再存在,因此它将尝试更新不存在的组件,并因该错误而失败。

对于第一个问题,一个可能的解决方案是为结果和比赛使用两个isLoading-variables。

对于第二个问题(在unmountComponent上调用setState),它有点复杂,因为您需要以某种方式取消请求。我建议阅读更多内容,一般建议是使用redux之类的库将数据移出组件。如果您用Google cancel promise on unmount进行搜索,则会发现有关此问题的讨论。您还可以使用“ isMounted”变量来处理它,该变量将充当丑陋的补丁。

因此,一(或两个)请求完成后,将调用setState,然后重新呈现该组件。 isLoading现在为true,因此将使用结果(或竞赛)调用filterAndSort,但不会同时调用两者,因为第二个请求仍在等待处理。

最后,在您的render方法中,需要首先定义isLoading(我认为在您的代码中可以,但在问题中没有),并且is True比较可以更好地放在

if (isLoading) {而不是if (isLoading == True) {

答案 1 :(得分:0)

您是对的。提出多个请求时,最好的做法是先等待所有 all 解决,然后再继续。为此,您可以使用内置在ES6中的Promise库。另外,据我所知,为了获取数据,它的最佳做法是在componentWillMount()中进行。我还要补充一点,this的上下文在异步函数中时会发生变化。因此,在您的componentWillMount()中:

let promiseArr = [axios.get(URL_ONE), axios.get(URL_TWO)];
let _this = this;
Promise.all(promiseArr)
  .then((resp) => {
    // Call _this.setState() here. Response from URL_ONE  
    // will be available in resp.data[0] and URL_TWO in resp.data[1]
  }).catch((err) => {
    console.log(err);
  })

此外,在您的构造函数中:

constructor(){
    super()
    this.state = {
      results: [],
      races: [],
      arr = []
      isLoading: true
    };
    this.setDefault = this.setDefault.bind(this);
    this.filterAndSort = this.filterAndSort.bind(this);
}

在调用这些方法时,使用.bind确保this的上下文引用该类的当前实例。

我认为做完这些事情后,错误就会消失。希望有帮助。