Source:  Twitter logo

When I do a search on this issue, I can only find questions that modify this.state directly somewhere in a method body instead of using this.setState(). My problem is that I want to set a starting state in the constructor as follows:

export default class Square extends React.Component<any, any> {
  constructor(props: any) {
    super(props);
    this.state = {
      active: false
    };
  }

  public render() {
    ...
  }
}

The app fails to start with the following compile error:

Cannot assign to 'state' because it is a constant or a read-only property

And this is because in definition of React.Component we have:

readonly state: null | Readonly<S>;

So I'm not sure how to go about this. The official react tutorial in JS directly assigns to this.state and says that it is an acceptable pattern to do so in the constructor, but I can't figure out how to do this with TypeScript.

Before rolling back (as suggested by @torvin's answer), please read through https://github.com/DefinitelyTyped/DefinitelyTyped/pull/26813#issuecomment-400795486.

This was not meant to be a regression - the solution is to use state as a property. It is better than previous approach (setting state in a constructor) because:

  • you don't need constructor at all anymore
  • you can't forget to initialize state (it is now a compile-time error)

For example:

type Props {}

type State {
  active: boolean
}

export default class Square extends React.Component<Props, State> {
  public readonly state: State = {
    active: false
  }

  public render() {
    //...
  }
}

Another approach:

type Props {}

const InitialState = {
  active: false
}

type State = typeof InitialState

export default class Square extends React.Component<Props, State> {
  public readonly state = InitialState

  public render() {
    //...
  }
}
42 users liked answer #0dislike answer #042
Nikola Mihajlović profile pic
Nikola Mihajlović

This appears to be a recent change in @types/react introduced in commit 542f3c0 which doesn't work very well, considering the fact that Typescript doesn't support assigning parent's readonly fields in derived constructors.

I suggest rolling back to a previous version of @types/react. Version 16.4.2 appears to be the last one before the unfortunate change has been made.

You can pin the version by removing the ^ in your package.json:

"devDependencies": {
  ...
  "@types/react": "16.4.2",

Also check out the discussion about this change on DefinitelyTyped github pull request page

35 users liked answer #1dislike answer #135
torvin profile pic
torvin

Because state is a readonly property of the component, it can't be set field-by-field, but you can still do:

constructor(props: MyProps) {
  super(props);
  this.state = {
    // include properties here
  }
}
-2 users liked answer #2dislike answer #2-2
Michael Landis profile pic
Michael Landis

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).