Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should react/no-did-mount-set-state trigger on ES2017 async componentDidMount? #1110

Open
bartolkaruza opened this issue Mar 10, 2017 · 16 comments · May be fixed by #1996
Open

Should react/no-did-mount-set-state trigger on ES2017 async componentDidMount? #1110

bartolkaruza opened this issue Mar 10, 2017 · 16 comments · May be fixed by #1996

Comments

@bartolkaruza
Copy link

bartolkaruza commented Mar 10, 2017

The rule react/no-did-mount-set-state triggers error on async componentDidMount function for a deferred setState. Basically, this.setState() is being called here after an async operation has been completed. It is not re-rendering straight after mounting, but only after some data has been retrieved from a server. Does it make sense to error on this?

async componentDidMount() {
    try {
      await someAsyncOperation();
    } catch (e) {
      this.setState({errorMessage: 'Failed to load data!'});
    }
  }
@ljharb
Copy link
Member

ljharb commented Mar 11, 2017

Duplicate of/related to #56

@bartolkaruza
Copy link
Author

bartolkaruza commented Mar 12, 2017

I agree it is related, but the fix for #56 at 70fef71 will not cover the async await case. Could similar logic be added to any calls to setState after an await within the componentDidMount body?

@ljharb
Copy link
Member

ljharb commented Mar 12, 2017

Yes, that seems reasonable.

@ljharb ljharb changed the title Should react/no-did-mount-set-state trigger on ES7 async componentDidMount? Should react/no-did-mount-set-state trigger on ES2017 async componentDidMount? Mar 12, 2017
@floriancargoet
Copy link

I believe that unfortunately, we can't be sure that a given statement is after an await statement:

async componentDidMount() {
    try {
      if (condition) {
        await someAsyncOperation();
      }
      this.setState({ ... }); // here
    } catch (e) {
      this.setState({ ... });
    }
  }

Depending on the condition, the line marked // here can be executed synchronously or asynchronously.

We can verify this by executing this snippet:

async function f(condition) {
    if (condition) {
      await 1;
    }
    console.log("after condition");
}
// will log "after call", then "after condition", proving that the execution was asynchronous
f(true); 
console.log("after call");

// will log "after condition", then "after call", proving that the execution was synchronous
f(false); 
console.log("after call");

@ljharb
Copy link
Member

ljharb commented Jun 7, 2018

No, the spec for await means that the line after the await will be async no matter what is returned.

@floriancargoet
Copy link

I haven't written anything about a "returned" value. I'm not saying that await somethingSync() is synchronous.
I'm talking about a not executed await expression. Note that the await line is in an if statement.
I was surprised by the results, I was expecting an async execution even if the await line isn't executed.
Please run my example in the Chrome console and see for yourself.

@ljharb
Copy link
Member

ljharb commented Jun 7, 2018

ahh, i see what you mean. sure, async functions execute synchronously until the first await, and if that code path isn't traversed, it will remain sync.

@floriancargoet
Copy link

Exactly. Because of that we can't always statically determine if a setState() is called asynchronously or not. I don't see how we could implement the suggested rule except for the simple cases (e.g. a setState immediately after an await).

@ljharb
Copy link
Member

ljharb commented Jun 7, 2018

You're right; we'd have to treat a setState as sync unless all possible code paths included at least one await.

@tgroutars
Copy link

tgroutars commented Jun 7, 2018

What about an option to never trigger when componentDidMount is async ?
It's clearly not perfect, lets the user shoot himself in the foot but less so than turning off the rule altogether

@slorber
Copy link

slorber commented Aug 16, 2018

I would also like such an option. When componentDidMount is async it's rarely the case that the user would try to do a setState before having waited for some promise. The other alternative for me is to simply disable this rule, as it's annoying for the devs using async/await

@ljharb
Copy link
Member

ljharb commented Aug 16, 2018

Rarely isn’t the same as never; it’s better to overwarn than underwarn here imo.

@slorber
Copy link

slorber commented Aug 16, 2018

@ljharb not saying to make it a default, just an option to opt-out for overwarn

@ljharb
Copy link
Member

ljharb commented Aug 16, 2018

I’d prefer to fix the default so it properly warns.

@frederikhors
Copy link

@ljharb any news on this? Can I help? What do you mean with

I’d prefer to fix the default so it properly warns.

@ljharb?

Can we use setState in an async componentDidMount() after an await something()?

@ljharb
Copy link
Member

ljharb commented Sep 14, 2018

There's no news; the state of this issue is that it is still awaiting a PR.

idanen added a commit to idanen/eslint-plugin-react that referenced this issue Sep 25, 2018
@idanen idanen linked a pull request Sep 25, 2018 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

6 participants