【问题标题】:Javascript/React: How to detect `await` call and setState accordingly?Javascript/React:如何检测`await`调用并相应地设置状态?
【发布时间】:2018-12-24 12:10:27
【问题描述】:

我有一个函数foo,它可能会或可能不会调用多个异步请求以缓存令牌:

async foo() {
    let token = this._token;

    if(!token) {
        token = await this.getTokenFromStorage();
    }        

    if(Tokens.isExpired(this._token) {
        this._token = await Tokens.refresh(this._token);
    } else {
        this._token = token;
    }

    return this._token;
}

现在,我希望调用 foo 的 React 组件具有如下逻辑:

  1. 调用 foo()
  2. 如果 foo 有缓存令牌,setState{isLoading: false}
  3. 否则,setState{ isLoading: true },并等待异步操作完成。异步操作完成后调用setState{ isLoading: false }并返回新缓存的令牌...

不过,对于如何执行此操作非常迷茫……感谢您的帮助。

【问题讨论】:

  • 似乎您可以在foo 的开头将isLoading 状态设置为true,在结尾设置为false
  • 你为什么关心它是否从缓存中检索?在调用 foo 之前总是将 loading 设置为 true 要容易得多。
  • 我觉得这会触发“React”不必要的重新渲染,因为即使令牌被缓存,我也会设置isLoading: true,然后立即以相同的方法将其设置回false ...似乎很浪费。
  • @Cod3Citrus 好吧,您的替代方案是将函数的内部暴露到更高级别,这会破坏您的透明缓存尝试。当然你可以这样做,但你会将你的功能耦合到表示层。 react 的部分乐趣在于编写被渲染的低效代码。请记住,仅仅因为 react 重新渲染并不意味着您会获得 dom 刷新,这是缓慢的部分。
  • 是 foo 在您要更改其状态的组件之外吗?

标签: javascript async-await es6-promise ecmascript-2016


【解决方案1】:

我不完全确定您是如何构建应用程序的,但也许以下内容为您提供了一些线索,说明如何实现与您的 async foo() 方法相关的 loading 状态更改:

  class YourReactComponent extends React.Component {

    constructor(props) {       
      // Set the initial default state of your component. 
      // token added as an optional extra for the purpose of this example
      this.state = {
        loading : false,
        token : ''
      }
    }

    async getTokenFromStorage() {
      // ... your implementation
    }

    // Modifed version of your foo method
    async foo(token) {

        // Condition to determine if loading state should be updated
        // We will use this to avoid redundant setState calls
        const shouldLoad = !token || Tokens.isExpired(token);

        // Check to see if we need to update loading state
        if(shouldLoad) {
          this.setState({ loading : true })
        }

        if(!token) {
          token = await this.getTokenFromStorage();
        }        
        else if(Tokens.isExpired(token)) {
          token = await Tokens.refresh(token);
        }

        // If loading state was previously updated, reset it
        if(shouldLoad) {
          this.setState({ loading : false })
        }

        return token;
    }

    // A render method I have created to demonstrate the concepts at hand
    render() {

      const { loading, token } = this.state

      return (<div>
          <button onClick={ async () => console.log(`Got this token: ${ this.foo(token) }`) }>load token</button>
          <p>{ loading ? 'Loading' : 'Ready' }</p>
        </div>)
    }
  }

要记住的关键概念是:

  • setState(..) 方法可在 React.Component 实例中使用
  • 调用setState(..) 用于更新组件的内部状态
  • 调用setState(..) 也会导致组件(以及受影响的子组件)的重新渲染。

有关.setState() 方法的更多信息,请参阅the documentation

【讨论】:

  • 嘿@Dacre 感谢您的回复。如果令牌已经被缓存,我打算弄清楚是否有避免将 isLoading 设置为 true 的情况。
  • 您好,不客气-感谢您的澄清。我刚刚更新了我的答案。这是否有助于回答您的初始问题?
猜你喜欢
  • 2019-10-27
  • 2016-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-28
  • 2022-07-29
  • 2022-11-06
  • 1970-01-01
相关资源
最近更新 更多