Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

allow typescript compiler to call setState on only one react state property

I'm using Typescript with React for a project. The Main component gets passed state with this interface.

interface MainState {   todos: Todo[];   hungry: Boolean;   editorState: EditorState;  //this is from Facebook's draft js } 

However, the code below (only an excerpt) won't compile.

class Main extends React.Component<MainProps, MainState> {   constructor(props) {     super(props);     this.state = { todos: [], hungry: true, editorState: EditorState.createEmpty() };   }   onChange(editorState: EditorState) {     this.setState({       editorState: editorState     });   } } 

The compiler complains that, in the onChange method where I am only trying to setState for one property, the property todos and the property hungry is missing in type { editorState: EditorState;}. In other words, I need to set the state of all three properties in the onChange function to make the code compile. For it to compile, I need to do

onChange(editorState: EditorState){   this.setState({     todos: [],     hungry: false,     editorState: editorState   }); } 

but there's no reason to set the todos and the hungry property at this point in the code. What is the proper way to call setState on only one property in typescript/react?

like image 526
Leahcim Avatar asked May 18 '16 13:05

Leahcim


2 Answers

Edit

The definitions for react were updated and the signature for setState are now:

setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void; 

Where Pick<S, K> is a built-in type which was added in Typescript 2.1:

type Pick<T, K extends keyof T> = {     [P in K]: T[P]; } 

See Mapped Types for more info.
If you still have this error then you might want to consider updating your react definitions.

Original answer:

I'm facing the same thing.

The two ways I manage to get around this annoying issue are:

(1) casting/assertion:

this.setState({     editorState: editorState } as MainState); 

(2) declaring the interface fields as optional:

interface MainState {     todos?: Todo[];     hungry?: Boolean;     editorState?: EditorState; } 

If anyone has a better solution I'd be happy to know!


Edit

While this is still an issue, there are two discussions on new features that will solve this problem:
Partial Types (Optionalized Properties for Existing Types)
and
More accurate typing of Object.assign and React component setState()

like image 81
Nitzan Tomer Avatar answered Sep 18 '22 14:09

Nitzan Tomer


Update state explicitly, example with the counter

this.setState((current) => ({ ...current, counter: current.counter + 1 })) 
like image 38
Alex Craft Avatar answered Sep 20 '22 14:09

Alex Craft