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?
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.
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.
We can use Pick
to ensure that you're setting a key that has been defined in your IState
interface.
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
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With