【问题标题】:React state in render is unavailable inside return渲染中的反应状态在返回中不可用
【发布时间】:2019-05-08 13:54:56
【问题描述】:

我有这些方法可以进行一些获取,然后一旦完成,它们就会设置状态。但是渲染是在状态完成之前调用的,并且不会更新。

以下内容似乎可以自行完成,但需要一分钟才能完成。

     //returns an promise with Array
    getTopIDs(url) {
        return fetch(url).then(blob => blob.json()).then(json => json)
    }

     // makes a URL fetchs JSON and return promise with single ID
    getStory(id) { 
        let url = `https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`
        return fetch(url).then(blob => blob.json()).then(json => json)
    }

   // call above methods, set state when done
    componentDidMount() { //
        let arr = []
        let promise = new Promise((resolve, reject) => {
            let data = this.getTopIDs("https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty").then((idArr) => {
                idArr.forEach((id, index) => {
                    this.getStory(id).then(res => {
                        arr.push(res)
                    })
                })
        //resolve once all pushed to arr
                resolve(arr)
            })
        })
    // set state once array is completed
        promise.then(res => {
            return this.setState({data: arr})
        })
    }

然后在下面的渲染中记录“否”、“否”并停止。在返回之外尝试它会记录“否”,“是”。搜索其他帖子我尝试在完成后设置一个布尔值并使用状态回调,但这些都不起作用(完全披露:我不太了解 setState 回调选项)

render() {
        return (
        <div>
            {
                this.state.data.length
                    ? console.log('yes')
                    : console.log('no')
            }
        </div>)
    }

我需要渲染来处理 this.state.data 仅在完成后。我该怎么做?

添加小提琴:https://jsfiddle.net/drumgod/e2atysu3/6/

【问题讨论】:

    标签: javascript reactjs state


    【解决方案1】:

    您的方法this.getStory() 是异步的,但您对数组创建的处理在您的承诺中是同步的。

    您需要使用async/await 或仅在idArr.forEach() 确定完成后运行resolve(arr)(使用Promise.all(idArr.map(...)) 可能更容易,其中...this.getStory() 返回结果)。

    【讨论】:

    • 我尝试实现您的想法,但速度非常慢。以下仅返回 30 个结果,需要 2 秒以上。 jsfiddle.net/drumgod/hu9qpts4/1 你能看看我如何优化它吗?它是不可用的,因为它是
    【解决方案2】:

    这就是你想要在getStory 中设置你的状态的方式:

    this.setState(prevState => ({
      data: [...prevState.data, res]
    }))
    

    如 cmets 中所述,这将为 forEach 中的每个数据点呈现组件。

    为了避免这个问题,componentDidMount() 的格式应该是这样的:

    componentDidMount() {
      const arr = [];
      this.getTopIDs("https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty").then((idArr) => {
        idArr.forEach((id, index) => this.getStory(id).then(res => arr.push(res)));
        this.setState(prevState => ({ data: [...prevState.data, arr] }))
      })
    }
    

    这还可以让您摆脱最后的promise.then 调用。

    【讨论】:

    • 这是完全有效的,只要 OP 理解每次下拉结果时都会重新渲染。因此,如果它是 100 个项目的列表,您将重新渲染整个列表 100 次
    • @Deryck 正确。我正在更新我的答案以进行相应调整。
    • 您的答案与 OP 存在相同的缺陷。您需要使用Promise.all(idArr.map(id =&gt; this.getStory(id))).then(res =&gt; res) 或类似的东西来避免.forEach 内的竞争条件
    • @fspy 我试过了。内容不呈现。请看jsfiddle.net/drumgod/hu9qpts4/16
    • 我检查了一下,好像我在setState 上犯了一个错误。要使您的示例正常工作,您必须查找this.state.data[0],然后您将获得可用的故事。请记住,每次获取故事时都会呈现此效果。
    猜你喜欢
    • 2021-11-20
    • 1970-01-01
    • 2018-12-03
    • 1970-01-01
    • 1970-01-01
    • 2021-01-17
    • 1970-01-01
    • 2017-06-24
    • 2019-11-19
    相关资源
    最近更新 更多