【问题标题】:Calculate new state by old state and result of a promise按旧状态和承诺结果计算新状态
【发布时间】:2019-01-29 22:06:37
【问题描述】:

我有一个项目不支持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 语法?


注意(更新):

由于当前的两个答案都包含相同的错误,please read @dhilt answer + responses首先解释什么是错误。

【问题讨论】:

    标签: javascript reactjs promise async-await


    【解决方案1】:

    首先,在setState() 中使用await 是不可能的。不是因为你的项目的限制,而是因为每个带有await 的函数都应该事先声明为async。它应该使用await 表示法或.then() 链接调用。所以传入.setState 的回调应该是async 并使用await 调用,但我们知道React 不能以这种方式工作。 React 期望普通函数并在没有 await.then() 的情况下调用它。它看起来像react-dom 模块中的func.apply(context, funcArgs)。也没有检查它是否返回 Promise。

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

    那么你可以做什么:

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

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

    【讨论】:

    • 你是说不应该从 setState 函数内部做任何副作用?好吧,我的副作用总是旧状态定义。我应该将所有这些状态移动到 redux 并且只调用 setState 并使用 redux 的最终状态?
    • 是的,副作用和在内部使用异步操作会使事情变得更糟。等待直到请求完成,然后才调度 redux 操作或调用.setState。即便如此,我在最后描述了一个挑战(以确保您对最近的请求作出回应)。
    • 至于“应该是 redux 还是 .setState”的问题,我无法真正回答。这取决于。 afaik redux 鼓励将所有应用程序状态保存在一个地方,但肯定存在例外。假设您要为页面上的多个输入字段加载自动完成建议,您最好不要将所有这几十个独立列表移动到单个应用程序商店中。
    【解决方案2】:

    如果问题中的代码是正确的,并且您只想清除 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 });
    });
    

    【讨论】:

    • 我可以从 setState 返回一个承诺吗?
    • 请记住,我没有看到 setState() 只是一个如何调用它的示例。
    • 对不起,我没听懂你想说什么。你的代码在反应中有效吗?
    • 我不知道。
    • 这个答案给了 OP 正是他所要求的 - “我怎样才能将它转换为非异步等待语法?”所以Downvoter,请有你的信念的勇气并发表评论以支持你的批评。如果您能发现一些可以改进答案的方法,那么我很乐意这样做。
    【解决方案3】:

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

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

    【讨论】:

    • 这可能会导致错误。请阅读:medium.freecodecamp.org/…
    • 您希望我的方法会出现哪些错误?
    • 如果我多次执行相同的代码而没有每次执行 setState,那么每次执行的第一行将具有相同的值。这就是为什么它必须通过给 setState 一个函数从 prevState 中计算出来。
    【解决方案4】:

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

    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
        }
      });
    }
    

    【讨论】:

    • 这可能会导致错误。请阅读:medium.freecodecamp.org/…
    • 实际上在这种情况下您不需要 setState 的功能版本。只需设置images 即可
    • @StavAlfi 你能具体说明你的意思吗?如果您预计在对服务器的请求正在进行时可以更改 this.state - 那么您没有办法阻止这种情况。 batch update 也不会影响逻辑,因为您不在this.setState 内部对this.state 进行操作...您的意思是别的吗?
    • @StavAlfi 您认为这种方法会出现什么问题?
    • 如果我多次执行相同的代码而没有每次执行 setState,那么每次执行的第一行将具有相同的值。这就是为什么它必须通过给 setState 一个函数从 prevState 中计算出来。
    猜你喜欢
    • 2021-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-13
    • 2017-10-25
    • 2019-07-20
    • 2021-11-26
    • 2016-09-24
    相关资源
    最近更新 更多