Have been converted a project from JavaScript to TypeScript. When the login button of the component is clicked the onSumbit even fires but the redux action is not dispatched. The vanilla JavaScript version of this works fine. The project does compile normally when built, with no errors.
The TypeScript file:
interface IErrors {
email: string | undefined;
password: string | undefined;
}
interface IUser {
email: string;
password: string;
}
interface IState {
email: string;
password: string;
[key: string]: string;
}
interface IReduxProps {
errors: IErrors;
}
interface IDispatchProps {
loginUser: (user: IUser, history: any) => (dispatch: any) => Promise<any>;
clearErrors: () => { type: string };
}
interface IProps extends IReduxProps {
loginUser: (user: IUser, history: any) => (dispatch: any) => Promise<any>;
clearErrors: () => { type: string };
}
export class Login extends React.Component<IProps, IState> {
state = {
email: '',
password: '',
};
onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const user = {
email: this.state.email,
password: this.state.password,
};
this.props.loginUser(user, history);
};
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ [e.target.name]: e.target.value });
};
componentWillUnmount() {
this.props.clearErrors();
}
render() {
return (
<div className={styles.page}>
<div className={styles.content}>
<h1 className={styles.title}>Login</h1>
<form className={styles.form} onSubmit={this.onSubmit}>
<div className={styles.inputGroup}>
<label className={styles.label} htmlFor="email">
Email
</label>
<input
className={classNames(styles.input, {
[styles.inputError]:
this.props.errors && this.props.errors.email,
})}
type="text"
name="email"
id="email"
value={this.state.email}
onChange={this.onChange}
autoComplete="email"
/>
{this.props.errors && this.props.errors.email ? (
<p className={styles.error}>{this.props.errors.email}</p>
) : null}
</div>
<div className={styles.inputGroup}>
<label className={styles.label} htmlFor="password">
Password
</label>
<input
className={classNames(styles.input, {
[styles.inputError]:
this.props.errors && this.props.errors.password,
})}
type="password"
name="password"
id="password"
value={this.state.password}
onChange={this.onChange}
autoComplete="password"
/>
{this.props.errors && this.props.errors.password ? (
<p className={styles.error}>{this.props.errors.password}</p>
) : null}
</div>
<button className={styles.button} type="submit">
Login
</button>
</form>
<Link className={styles.link} to="/register">
I need an account
</Link>
</div>
</div>
);
}
}
// TODO: change state to match redux state interface
const mapStateToProps = (state: any): IReduxProps => ({
errors: state.errors,
});
const mapDispatchToProps = (): IDispatchProps => ({
loginUser,
clearErrors,
});
export default connect(mapStateToProps, mapDispatchToProps)(Login);
The redux action:
export const loginUser = (userData, history) => dispatch => {
return axios
.post('/api/login', userData)
.then(res => {
const { token, id } = res.data;
localStorage.setItem('token', token);
setAuthToken(token);
dispatch({ type: 'LOGIN', payload: { id, token } });
history.push('/dashboard');
dispatch({ type: 'CLEAR_ERRORS' });
})
.catch(err =>
dispatch({
type: 'GET_ERRORS',
payload: err.response.data,
}),
);
};
Any help would be much appreciated