按旧状态和承诺的结果计算新状态



我有一个不支持generatorsasync await语法的项目。

我用async await构建了以下代码,因为我看不到任何其他方法可以做到这一点:

this.setState(async lastState => {
const newImagesAmount = lastState.images.length + 20;
const newImages = await this.getImages(this.props.tag, newImagesAmount);
return {
images: newImages
};
});

为什么?在这种特殊情况下,新状态是由旧状态和promise的结果构建的。

如何将其转换为非async await语法?


注意(更新(:

由于当前两个答案都包含相同的错误,请先阅读@dhilt答案+响应,其中解释了错误是什么。

首先,不可能在setState()内部使用await。不是因为项目的限制,而是因为每个包含await的函数都应该事先声明为async。它应该用await符号或.then()链接来调用。所以传递给.setState的回调应该被async并与await一起调用,但我们知道 React 不是这样工作的。React 期望普通函数并在没有await.then()的情况下调用它。它看起来像func.apply(context, funcArgs)react-dom模块。也没有检查它是否返回承诺。

因此,让我们回到您的最终目标。据我了解,这是为了确保加载延迟数据后状态仍然一致。这是另一个限制。除了同步XHR(这是非常糟糕的做法(之外,没有办法暂停所有执行,直到数据到来。也就是说:无论你做什么,你都不能100%确定state没有在"请求已发送"和"数据已收到"步骤之间发生突变。无论是await,还是功能setState都不允许冻结执行。是的,您的代码仍然引用"prevState"变量,但它可能不再是实际组件的状态!

所以你可以做什么:

  1. 在将另一个 XHR 发送到同一端点之前中止正在进行的 XHR(应该适用于读取操作;对于写入操作没有意义(
  2. 在处理响应时解析请求参数以有效方式将其注入状态(例如,第一个响应用于 1..10 图像,下一个响应用于 11..20 - 因此您可以使用它并将第二个响应注入适当的索引,使前 10 个仍然未初始化(
  3. 设置一些标志以阻止新请求,直到正在进行的请求完成

附言因此,从函数setState内部发出请求会导致混乱,并且不会增加任何额外的灵活性。

如果问题中的代码是正确的,并且您只想清除 async/await 以支持传统的return promise.then(...)语法,请执行以下操作:

this.setState(lastState => {
const newImagesAmount = lastState.images.length + 20;
return this.getImages(this.props.tag, newImagesAmount)
.then(newImages => ({ 'images': newImages });
});

以下简化也可能有效:

this.setState(lastState => {
return this.getImages(this.props.tag, lastState.images.length + 20)
.then(images => ({ images });
});

我假设这里的lastState实际上只是您更新之前的当前状态。尝试这样的事情:

const imageLength = this.state.images.length + 20
this.getImages(this.props.tag, imageLength)
.then(newImages => {
this.setState({images: newImages})
}

我想你可能想要这样的东西

const newImagesAmount = this.state.images.length + 20;
this.getImages(this.props.tag, newImagesAmount).then(newImages => 
this.setState(prevState => ({
..prevState,
images: newImages
})
);

首先,您请求新图像,此请求基于当前状态。然后(当结果到达时(更新状态。


更新的版本,允许在请求完成之前保护异步逻辑免受多次执行

if (!this.state.pending) {
this.setState(lastState => {
const newImagesAmount = lastState.images.length + 20;
this.getImages(this.props.tag, newImagesAmount).then(newImages => 
this.setState(prevState => ({
...prevState,
pending: false,
images: newImages
})
).catch(() =>
this.setState(prevState => ({
...prevState,
pending: false
})
);
return {
...lastState,
pending: true
}
});
}

最新更新