Javascript 为什么我需要使用setState回调来设置依赖于第一项的第二个状态项的状态';什么是状态整理?

Javascript 为什么我需要使用setState回调来设置依赖于第一项的第二个状态项的状态';什么是状态整理?,javascript,reactjs,Javascript,Reactjs,在这个componentDidUpdate方法中,在执行setState以将引号设置为从提取返回的内容之后,我必须使用回调第二次执行setState以将randomQuoteIndex设置为调用randomQuoteIndex的结果,这依赖于this.state.quotes.length,即: componentDidMount() { fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2e

在这个componentDidUpdate方法中,在执行setState以将引号设置为从提取返回的内容之后,我必须使用回调第二次执行setState以将randomQuoteIndex设置为调用randomQuoteIndex的结果,这依赖于
this.state.quotes.length
,即:

componentDidMount() {
    fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
      // Takes a JSON response string and parses it into JS object
      .then(response => response.json())
      // state is set to quotes: quotes due to destructuring
      // Using setState callback since setState is asynchronous and need to make sure quotes is loaded before setting the randomQuoteIndex state since it depends on it
      .then(quotes => this.setState({ quotes }, () => {
        this.setState({
          randomQuoteIndex: this.randomQuoteIndex(),
          isDoneFetching: true
        })
      }))
  }
为什么下面的代码不起作用?根据所选的答案,我的印象是,在为第一项设置状态之后,才会应用setState中的第二项。如果我尝试这样做,我会得到一个错误“TypeError:无法读取undefined的属性'quote'”(我读到setState是异步的,关于何时使用回调,但我很难理解我读到的内容/它在这种情况下如何应用。)

以下是完整的组件代码(工作版本):

import React,{Component}来自'React';
导入“/App.css”;
从“lodash”导入{random};
从“./components/Button”导入按钮;
类应用程序扩展组件{
建造师(道具){
超级(道具);
此.state={
报价:[],
randomQuoteIndex:null,
isDoneFetching:false
}
}
componentDidMount(){
取('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
//获取JSON响应字符串并将其解析为JS对象
.then(response=>response.json())
//状态设置为quotes:quotes由于解构
//使用setState回调,因为setState是异步的,并且需要确保在设置randomQuoteIndex状态之前加载引号,因为它依赖于它
.then(quotes=>this.setState({quotes},()=>{
这是我的国家({
randomQuoteIndex:this.randomQuoteIndex(),
isDoneFetching:正确
})
}))
}
获取随机引用(){
返回this.state.quotes[this.state.randomQuoteIndex];
}
randomQuoteIndex(){
返回random(0,this.state.quotes.length-1);
}
render(){
返回(
{this.state.isDoneFetching?this.randomQuote.quote:'正在加载…'}
);
}
}
导出默认应用程序;

并不是setState是异步的,而是在设置状态之前调用
randomQuoteIndex
的结果。无论是否进行异步状态更新,都会出现这种情况。考虑这个稍微重构的版本<代码>组件DealStuts<代码>:

  componentDidMount() {
    fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
      .then(response => response.json())
      .then(quotes => {
        const newState = {
          randomQuoteIndex: this.randomQuoteIndex(),
          isDoneFetching: true,
          quotes
        }
        this.setState(newState)
      })
  }
这在功能上与您在问题中发布的版本完全相同。希望这会突出显示
this.randomQuoteIndex()
是在
this.setState(newState)
之前进行计算的,因为
setState
尚未调用,所以不存在
randomQuoteIndex
所依赖的状态。调用
setState
时,必须先计算参数,然后才能将其传递到
setState
,无论是否同步,都不会在调用
randomQuoteIndex
时进行更新

解决此问题的简单方法是使
randomQuoteIndex
将引号列表作为参数,而不是将其从组件状态中拉出。重写后,这对方法可能如下所示:

  componentDidMount() {
    fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
      .then(response => response.json())
      .then(quotes => this.setState({
          quotes,
          randomQuoteIndex: this.randomQuoteIndex(quotes),
          isDoneFetching: true
        }));
  }

  randomQuoteIndex(quotes) {
    return random(0, quotes.length - 1);
  }

它只需要调用一次
setState
,并且(可能)为您保存一次重新渲染。

就个人而言,我并不认为React的作者在
setState
中回调的意图是使用它来调用下一个
setState
。 为什么不试试上面提到的@Icepickle:

函数randomQuoteIndex(引号){
返回random(0,quotes.length-1);
}
...
...
.然后(引号=>{
这是我的国家({
引用,
randomQuoteIndex:randomQuoteIndex(引号),
isDoneFetching:正确
})
}))
...

仅更新一次状态=>确保始终只有一个渲染周期

那么,回调将确保已应用上一个状态。问题是,为什么您的
randomQuoteIndex
依赖于一个状态属性,而不是检索一个数组作为一个参数,这将是一个更可重用的概念(当时它甚至不需要再位于组件内部),而错误来自于此?它是否发生在
render()
中?我也不明白为什么您会依赖getter属性来检索引号,但随后请求引号的引号:)我猜罪魁祸首是回调中的
this
引用。为了验证,您是否可以将
randomQuoteIndex:this.randomQuoteIndex()
更改为类似于
randomQuoteIndex:Date.now()
的其他内容,并查看这是否会产生相同的错误?您是否可以尝试在引号字段中使用prevState,就像在回答此问题时一样?如果我们想在setState中保留对象上的字段,我们需要使用prevState或spread运算符。您的建议奏效了,但我不明白如何在这个.randomQuoteIndex(引号)和这个.randomQuoteIndex()中,调用依赖于首先设置的引号的状态。我错过了什么?谢谢你的帮助!如果你看一下重构后的
this.randomQuoteIndex(quotes)
,你会发现它实际上并没有在任何时候使用
this.state
quotes
被作为参数传入,这就是为什么它可以在
setState
更新组件状态之前被调用的原因。我看到它没有使用
this.state
,但我的困惑是,对于要调用的函数,它依赖于
quotes
状态作为参数。但我现在明白了,这只是因为
组件didmount
设置状态
中的所有内容都是立即应用的(甚至不是按顺序应用的),所以
引号
状态是在
设置状态
这个.randomQuoteIndex(引号)
中作为参数需要的同时设置的。不管是谁,你知道为什么吗<
  componentDidMount() {
    fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
      .then(response => response.json())
      .then(quotes => {
        const newState = {
          randomQuoteIndex: this.randomQuoteIndex(),
          isDoneFetching: true,
          quotes
        }
        this.setState(newState)
      })
  }
  componentDidMount() {
    fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
      .then(response => response.json())
      .then(quotes => this.setState({
          quotes,
          randomQuoteIndex: this.randomQuoteIndex(quotes),
          isDoneFetching: true
        }));
  }

  randomQuoteIndex(quotes) {
    return random(0, quotes.length - 1);
  }