Preact: setState not working because this._nextState is undefined

Created on 30 Jan 2020  路  5Comments  路  Source: preactjs/preact

In the constructor of my app I call an async function that does

this.setState({ loading: true })

This is the stack trace I get

util.js:9 Uncaught (in promise) TypeError: Cannot set property 'loading' of undefined
    at s (util.js:9)
    at App.d.setState (component.js:40)
    at App.somethingAsync (app.js:47)
    at new App (app.js:44)
    at T (index.js:67)
    at children.js:102
    at b (children.js:236)
    at b (children.js:231)
    at _ (children.js:60)
    at T (index.js:180))

In the the setState of component.js#L29 the this._nextState is undefined and s is also undefined.

I am using Preact with htm without any compilation, direct import in the browser with the script tag type module.

import { Component, h } from 'https://unpkg.com/[email protected]/dist/preact.module.js'
import htm from 'https://unpkg.com/htm?module'
const html = htm.bind(h)

export default class App extends Component {
  constructor (props) {
    super(props)
    this.somethingAsync = this.somethingAsync.bind(this)
    this.state = {
      loading: false
    }
    this.somethingAsync()
  }
  async somethingAsync () {
    this.setState({ loading: true })
    setTimeout(() => this.setState({ loading: false }), 100)
  }
}

This is the index.js

import { h, render } from 'https://unpkg.com/[email protected]/dist/preact.module.js'
import htm from 'https://unpkg.com/htm?module'
import App from './components/app.js'
const html = htm.bind(h)
render(
  html`<${App}  />`,
  document.getElementById('app')
)

What should I do?

Most helpful comment

Hey,

Usually we dispatch async stuff from componentDidMount since this is a bit safer, this because in constructor we are still creating everything while an async setState could happen during the construction of this part of the tree. In ComponentDidMount we've already established the semantics of this and are committing it to the DOM

However it shouldn't error out and I'll certainly look at it.

All 5 comments

Hey,

Usually we dispatch async stuff from componentDidMount since this is a bit safer, this because in constructor we are still creating everything while an async setState could happen during the construction of this part of the tree. In ComponentDidMount we've already established the semantics of this and are committing it to the DOM

However it shouldn't error out and I'll certainly look at it.

Ok thanks for the answer I will use the componentDidMount to make it work meanwhile.
I have here a fully working example to test it easily.
index.html

<!DOCTYPE html>
<html>
<head>
  <title>Test</title>
</head>
<body>
  <div id='app' />

  <script type='module' >
    import { Component, h, render } from 'https://unpkg.com/[email protected]/dist/preact.module.js'
    import htm from 'https://unpkg.com/htm?module'
    const html = htm.bind(h)
    class App extends Component {
      constructor (props) {
        super(props)
        this.somethingAsync = this.somethingAsync.bind(this)
        this.state = {
          loading: false
        }
        this.somethingAsync()
      }
      async somethingAsync () {
        this.setState({ loading: true })
        setTimeout(() => this.setState({ loading: false }), 100)
      }
    }

    render(
      html`<${App} />`,
      document.getElementById('app')
    )
  </script>

</body>
</html>

Firing async code during class instantiation is unusual as plain instantiation of objects should be as few as possible and not trigger side effects. It may work, but the intended way to do it is via componentDidMount like Jovi said 馃憤

It's advised, even for React to always start your async work on componentDidMount
https://daveceddia.com/where-fetch-data-componentwillmount-vs-componentdidmount/#componentdidmount

Closing as calling effects inside constructor is unsupported behaviour. Use componentDidMount or an useEffect call if you're using hooks instead. Calling effects in constructor is unsafe because we are still in the process of creating the tree and the component instance as @JoviDeCroock explained.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jasongerbes picture jasongerbes  路  3Comments

SabirAmeen picture SabirAmeen  路  3Comments

paulkatich picture paulkatich  路  3Comments

matuscongrady picture matuscongrady  路  3Comments

youngwind picture youngwind  路  3Comments