React.js-对if else和外部块进行排序

时间:2018-07-22 12:09:36

标签: javascript reactjs asynchronous

我有一个具有以下功能的react组件:

alreadyUpvoted() {
    return this.state.upvotes.indexOf(this.props.context.userId) !== -1
}

alreadyDownvoted() {
    return this.state.downvotes.indexOf(this.props.context.userId) !== -1
}

addUpvote() {
    this.setState(prev => ({
        upvotes: prev.upvotes.concat(this.props.context.userId),
        upvoted: true,
        votes: prev.votes + 1,
    }), () => {
        console.log('add upvote', this.state)
    })
}

removeUpvote() {
    var new_upvotes = this.state.upvotes.concat()
    new_upvotes.pop(this.props.context.userId)
    this.setState(prev => ({
        upvotes: new_upvotes,
        upvoted: false,
        votes: prev.votes - 1,
    }), () => {
        console.log('remove upvote', this.state)
    })
}

addDownvote() {
    this.setState(prev => ({
        downvotes: prev.downvotes.concat(this.props.context.userId),
        downvoted: true,
        votes: prev.votes - 1,
    }), () => {
        console.log('add dowvote', this.state)  
    })
}

removeDownvote() {
    var new_downvotes = this.state.downvotes.concat()
    new_downvotes.pop(this.props.context.userId)
    this.setState(prev => ({
        downvotes: new_downvotes,
        downvoted: false,
        votes: prev.votes + 1,
    }), () => {
        console.log('remove downvote', this.state)
    })
}

postVotesData() {
    var json = {
        upvotes: this.state.upvotes,
        downvotes: this.state.downvotes
    }
    json = JSON.stringify(json)
    console.log(json)
    const url = `/api/reddit/r/${this.props.subreddit}/posts/${this.props.postid}/`
    fetch(url, {
        method: 'PUT',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        body: json
    })
    .then(response => {
        console.log('response status:', response)
        return response.json()
    })
    .then(res => console.log('response data:', res))
}

我创建了一个toggleUpvote函数,该函数利用了上述所有函数:

toggleUpvote() {
    if (this.alreadyDownvoted()) {
        this.removeDownvote()
        this.addUpvote()
    }
    else if (this.alreadyUpvoted()) {
        this.removeUpvote()
    }
    else {
        this.addUpvote()
    }
    this.postVotesData()
}

这里的问题是this.postVotesData() if-else 块完成之前得到执行。

对于当前状态,控件应转到else块。但是,console.log中的this.postVotesData()console.log中存在的addUpvote之前被执行了!!

证据: enter image description here

在当前状态下,upvotes数组在执行 else 后应具有一个值,并且该数组应在 PUT 中使用。但是,空数组被放置,然后将值添加到数组中。

我还希望按顺序执行 if 块中的功能。我该怎么解决?

2 个答案:

答案 0 :(得分:2)

这就是异步javascript的工作方式。我相信您正在执行异步操作(例如提取调用),或进行React的setState()。 js中的它们(以及许多其他东西)都是异步的。 为了解决这个问题,您需要使用回调或Promise。使用Promise时,可以使用async-await语法来获得干净的代码。

async toggleUpvote() {
    if (this.alreadyDownvoted()) {
        await this.removeDownvote()
        await this.addUpvote()
    }
    else if (this.alreadyUpvoted()) {
        await this.removeUpvote()
    }
    else {
        await this.addUpvote()
    }
    await this.postVotesData()
}

为此,removeUpvote,addUpvote和postVotesData需要返回承诺。

如果在这些函数中调用setState,则需要提供对setState的回调。否则,您可以使用functional-setState模式。

一个避免此问题的简单解决方法是从您的函数中返回一个类似于以下内容的promise,并使用上面建议的async-await语法。

addUpvote() {
  return new Promise((resolve, reject) => {
    this.setState(prev => ({
        upvotes: prev.upvotes.concat(this.props.context.userId),
        upvoted: true,
        votes: prev.votes + 1,
    }), () => {
        console.log('add upvote', this.state)
        return resolve(); // Signal that the operation has finished
    })
  })
}

答案 1 :(得分:1)

它可能看起来像这样,但是在完成块之前不会被执行。所有这些功能都是按照确切的顺序(同步)启动 ,具体取决于if / else路由。

您可能会这么认为,是因为这些功能可能是异步的,例如AJAX请求。这些与承诺和/或完成功能一起工作。您可能需要查找有关此的一些教程,因为这里可以通过简单的答案进行广泛解释。
简而言之:保证您告诉javascript等待结果,然后再继续。

编辑:我注意到react标签,您可能想签出await。可能需要读一些书才能理解其中的内容,但值得进行研究。