Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript & ReactJS How To Dynamically Set State

I have this interface defined as my state:

interface State {
  id: string;
  name: string;
  description: string;
  dimensionID: string;
  file: File | null;
  operator: string;
  isFormValid: boolean;
  filename: string;
};

I have a simple on change handler:

  update = (event: React.FormEvent<HTMLInputElement>): void => {
    const { name, value } = event.currentTarget;
    this.setState({ [name]: value });
  };

However, this error gets thrown:

Error:(109, 19) TS2345: Argument of type '{ [x: string]: string; }' is not assignable to parameter of type 'State | Pick<State, "id" | "name" | "description" | "dimensionID" | "file" | "operator" | "isFormValid" | "filename"> | ((prevState: Readonly<State>, props: Readonly<Props>) => State | Pick<...>)'.
  Type '{ [x: string]: string; }' is not assignable to type '(prevState: Readonly<State>, props: Readonly<Props>) => State | Pick<State, "id" | "name" | "description" | "dimensionID" | "file" | "operator" | "isFormValid" | "filename">'.
    Type '{ [x: string]: string; }' provides no match for the signature '(prevState: Readonly<State>, props: Readonly<Props>): State | Pick<State, "id" | "name" | "description" | "dimensionID" | "file" | "operator" | "isFormValid" | "filename">'.

My question is: How can I set state dynamically like how my on change handler tries? This handler is used for different parts of the form so I won't know which key of state I will need to update.

Update: SOLUTION From this post

  update = (event: React.FormEvent<HTMLInputElement>): void => {
    const { name, value } = event.currentTarget;
    this.setState(prevState => ({
      ...prevState,
      [name]: value,
    }))
  };

Works!

like image 404
Vincent Newkirk Avatar asked Oct 29 '18 06:10

Vincent Newkirk


3 Answers

Most prefered solution casting.

this.setState({[name]: value} as {[P in keyof State]: State[P]})
// or
this.setState({[name]: value} as Pick<State, keyof State>)

setState with spread previous state

this.setState((prevState) => ({ ...prevState, [name]: value }));

This doesn't check the types. It's just a quick workaround for anyone who is stuck transpiling.

this.setState<never>({[name]: value})

Related links

React setState

Github Tim Roes comment

like image 148
MenyT Avatar answered Sep 28 '22 02:09

MenyT


The constant name is widened to string. Which version of TS are you using?

You cast it like this, but it's not a good practice though:

this.setState({
  [name]: value
} as Pick<State, keyof State>);

Bug related to this issue:

  • https://github.com/Microsoft/TypeScript/issues/15534
  • https://github.com/Microsoft/TypeScript/issues/13948
  • https://github.com/DefinitelyTyped/DefinitelyTyped/issues/26635
like image 41
tottomotto Avatar answered Sep 28 '22 03:09

tottomotto


eg.

update = (name: string, event: React.FormEvent<HTMLInputElement>): void => {
    const { value } = event.currentTarget;
    this.setState({ [name]: value });
  };

<input onChange={this.update.bind(null, 'first_name')} value={this.state.first_name} />
like image 29
Alfred Ayi-bonte Avatar answered Sep 28 '22 04:09

Alfred Ayi-bonte