Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Form in React and TypeScript with DRY handle of change

I looking for best way manage state of form in React with TypeScript. My simple form have two values: login and password fields. I implement IState interface for form state and DRY handleChange method for store new value in state without recreating function in each render() execution.

interface IState {
  login: string;
  password: string;
}

class LoginForm extends React.Component<{}, IState> {
  public state = {
    login: "",
    password: "",
  };

  public render() {
    const { login, password } = this.state;
    return (
      <form>
        <input name="login" value={login} onChange={this.handleChange}/>
        <input name="password" value={password} onChange={this.handleChange} type="password"/>
      </form>
    );
  }

  private handleChange = (e: React.FormEvent<HTMLInputElement>) => {
    const { name, value } = e.currentTarget;
    const n = name as keyof IState;
    // @ts-ignore
    this.setState({
      [n]: value,
    });
  }
}

I use native HTML's name attribute for store field name. n variable will have only login or password value. Any other value is impossible. Can I tell TypeScript the n variable is "login" | "password" literals type? TypeScript regard n as string type variable even I use:

const n = name as keyof IState;

or

const n = name as "login" | "password";

Then, when I remove // @ts-ignore I get error:

Type error: Argument of type '{ [x: string]: string; }' is not assignable to parameter of type 'IState | Pick<IState, "login" | "password"> | ((prevState: Readonly<IState>, props: Readonly<IProps>) => IState | Pick<IState, "login" | "password"> | null) | null'.
Type '{ [x: string]: string; }' is missing the following properties from type 'Pick<IState, "login" | "password">': login, password  TS2345

but no error when I hardcode:

const n = "login";

Any way to force "login" | "password" type to n variable? Or any other solution for DRY handleChange without optimization issues and pure TypeScript?

like image 794
Dzwiedziu-nkg Avatar asked Feb 02 '19 13:02

Dzwiedziu-nkg


People also ask

Which is the recommended way of handling forms in React?

The standard way to handle form input value changes is to handle them with React. This is a technique called controlled components. We can create controlled components with input , textarea , and select elements. They maintain their own state, and updates are based on user input.

How do you handle form in React functional component?

We accomplish this by doing the following two steps: Whenever the input value is changed, call an event handler to update the component state to the new input value. Re render the the React Element with its value attribute set to the updated state input value.


2 Answers

We can use Pick to ensure that you're setting a key that has been defined in your IState interface.

Pick - set state

private handleChange = (e: React.FormEvent<HTMLInputElement>) => {
    const { name, value } = e.currentTarget;
    this.setState({
        [name]: value
    } as Pick<IState, keyof IState>);
};

Or alternative you can use Partial which will make all your state keys optional.

class App extends React.Component<{}, Partial<IState>> {
    // ... rest of component
}
like image 50
Win Avatar answered Oct 31 '22 12:10

Win


1.Declare your interface

interface IState {
  login: string;
  password: string;
}

2.Declare your state

const [myState, setMyState] = useState<IState>()

3.Declare your onchange function

const onChange=(e: any): void => {
    const { name, value } = e.currentTarget;
    setMyState({ ...myState, [name]: value });
}

4.Declare your markup

<input name="login" type="text" onChange={onChange}/>

We are using the spread function to maintain and update the state Note that you need to name the markup element with the "name" attribute and it should correspond with your interface attribute

Spread syntax

React naming elements

like image 41
Peppe426 Avatar answered Oct 31 '22 10:10

Peppe426