Login Form validation in Reactjs : material ui <textfield> - reactjs

I am trying to do login form validation in my react app. I am new to react and I am using Material UI. So I try to enter the data in the login and password fields but I am not able to. Could someone tell me what exactly is the problem? Is it because I declared the data object in state? Following is the code:
state = {
open: false,
show: null,
dialogOpen: false,
buttonDisabled: true,
data: {
email: "",
password: ""
},
errors: {}
};
handleChange = e =>
this.setState({
data: { ...this.state.data, [e.target.name]: e.target.value }
});
onSubmit = () => {
const errors = this.validate(this.state.data);
this.setState({ errors });
};
validate = data => {
const errors = {};
if (!Validator.isEmail(data.email)) errors.email = "Invalid email";
if (!data.password) errors.password = "Can't be blank";
return errors;
};
const { data, errors } = this.state;
<Dialog open={this.state.dialogOpen} onClose={this.closeDialog} >
<DialogTitle>Login</DialogTitle>
<DialogContent>
<DialogContentText>
Please enter your Login data here
</DialogContentText>
<form onSubmit={this.onSubmit}>
<TextField
margin="dense"
id="email"
label="Email Address"
className={classes.textField}
type="email"
value={data.email}
onChange={this.handleChange}
fullWidth
/>
{errors.email && <InlineError text={errors.email} />}
<TextField
margin="dense"
id="password"
label="Password"
className={classes.textField}
type="password"
value={data.password}
onChange={this.handleChange}
fullWidth
/>
{errors.password && <InlineError text={errors.password} />}
<Button
className={classes.button}
onClick={this.clickLogin}
color="primary"
>
Enter
</Button>
</form>
</DialogContent>
</Dialog>

the issue that i noticed with your code is that you are targeting name attribute which you didn't create. so make the following adjustment to your code
handleChange = e =>
this.setState({
data: { ...this.state.data, [e.target.id]: e.target.value }
});
onSubmit = () => {
const errors = this.validate(this.state.data);
this.setState({ errors });
};
In the above code, i used e.target.id which can be referenced correctly in the textField.

Related

How to display an Error message when logging in fails in Reactjs

How to display an Error message when logging in fails in Reactjs.
I want to display the alert message 'Invalid Username or Password, Please try again.' when the user logs in fails on the page. How can I do that in Reactjs?
the code:
login.js
export default function LogIn() {
let history = useHistory();
const initialFormData = Object.freeze({
username: '',
password: '',
});
const [formData, updateFormData] = useState(initialFormData);
const handleChange = (e) => {
updateFormData({
...formData,
[e.target.name]: e.target.value.trim(),
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
axiosInstance
.post(`token/`, {
username: formData.username,
password: formData.password,
})
.then((res) => {
localStorage.setItem('access_token', res.data.access);
localStorage.setItem('refresh_token', res.data.refresh);
axiosInstance.defaults.headers['Authorization'] =
'JWT ' + localStorage.getItem('access_token');
history.push("/home");
});
};
return (
<Box component="form" onSubmit={handleSubmit} noValidate>
<TextField
margin="normal"
required
id="username"
label="username"
name="username"
autoComplete="username"
autoFocus
onChange={handleChange}/>
<TextField
margin="normal"
required
name="password"
label="password"
type="password"
id="password"
autoComplete="current-password"
onChange={handleChange}/>
<Button type="submit" onClick={handleSubmit}>
LOG IN
</Button>
</Box>
);}
Thank you in advance.
you can use alert in javascript to show a dialog box also you can use this Pakage
to show alert or message to the user
you can use this code to set error in state and show in label
const [error,setError]=useState();
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
axiosInstance
.post(`token/`, {
username: formData.username,
password: formData.password,
})
.then((res) => {
localStorage.setItem('access_token', res.data.access);
localStorage.setItem('refresh_token', res.data.refresh);
axiosInstance.defaults.headers['Authorization'] =
'JWT ' + localStorage.getItem('access_token');
history.push("/home");
}, reason => {
console.error(reason); // Error!
setError('Invalid Username or Password')
});
};
return (
<Box component="form" onSubmit={handleSubmit} noValidate>
<TextField
margin="normal"
required
id="username"
label="username"
name="username"
autoComplete="username"
autoFocus
onChange={handleChange}/>
<TextField
margin="normal"
required
name="password"
label="password"
type="password"
id="password"
autoComplete="current-password"
onChange={handleChange}/>
<Button type="submit" onClick={handleSubmit}>
LOG IN
</Button>
{error?<Label>{error}</Label>:null}
</Box>
);

Antd form doesn't identify input values

I have created my react form with antd. I have added antd validation for the form. But my form doesn't know whether I have filled the form or not. Whenever I filled the form and submitted it, it doesn't call onFinish method. Instead it fails and calls onFinishFailed method and gives me validation error messages.
I have created it in correct way according to my knowledge. But there is something missing I think. Here's my code.
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const history = useHistory();
const [form] = Form.useForm();
const layout = {
labelCol: { span: 4 },
wrapperCol: { span: 8 },
};
const onChangeName = (e) => {
setName(e.target.value);
console.log(name);
}
const onAddCategory = (values) => {
let req = {
"name": values.name,
"description": values.description
}
postCategory(req).then((response) => {
if (response.status === 201) {
message.success('Category created successfully');
history.push('/categorylist');
}
}).catch((error) => {
console.log(error);
message.error('Oops, error occured while adding category. Please try again');
});
}
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
console.log('State:', name, description);
};
return (
<React.Fragment>
<Form
form={form}
name="control-hooks"
onFinish={onAddCategory}
onFinishFailed={onFinishFailed}
{...layout}
size="large"
>
<Form.Item
name="name"
rules={[
{
required: true,
message: 'You can’t keep this as empty'
}, {
max: 100,
message: 'The category name is too lengthy.',
}
]}
>
<label>Category name</label>
<Input
placeholder="Category name"
className="form-control"
value={name}
onChange={onChangeName}
/>
</Form.Item>
<Form.Item
name="description"
rules={[
{
required: true,
message: 'You can’t keep this as empty'
}, {
max: 250,
message: 'The description is too lengthy',
}
]}
>
<label>Description</label>
<Input.TextArea
placeholder="Description"
className="form-control"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</Form.Item>
<Form.Item shouldUpdate={true}>
<Button
type="primary"
htmlType="submit"
className="btn btn-primary"
>
Add category
</Button>
</Form.Item>
</Form>
</React.Fragment>
)
In this form I have managed state using hooks. In onFinishFailed method I have logged my input values with state and they have values. But form doesn't identify it.
How do I resolve this. Please help.
I found the issue. Here I had added label inside form item. It was the reason for the unexpected behavior. Once I took the label outside the form item problem was solved.
<label>Category name</label>
<Form.Item
name="name"
rules={[
{
required: true,
message: 'You can’t keep this as empty'
}, {
max: 100,
message: 'The category name is too lengthy.',
}
]}
>
<Input
placeholder="Category name"
className="form-control"
value={name}
onChange={onChangeName}
/>
</Form.Item>

React state sets the same value to all of the state values

I'm new to react JS, I'm trying to get the values from a form, and send it to a node JS middleware.
I set up two values in the state, one for the email and another one for the password. I set the state for both values in the set state method.
class LoginForm extends Component {
constructor(props){
super(props)
this.state = {
email : '',
password : '',
}
}
handleChange = (e) => {
this.setState({ email : e.target.value, password : e.target.value})
}
handleSubmit = (e) => {
console.log('state', this.state)
};
render () {
return (
<div style = {styles.form} >
<Fragment>
<Form
{...layout}
name="basic"
initialValues={{
remember: true,
}}
onFinish={this.handleSubmit}
>
<Form.Item
name="email"
rules={[
{
type: 'email',
message: 'The input is not valid E-mail!',
},
{
required: true,
message: 'Please input your E-mail!',
},
]}
hasFeedback
>
<Input
placeholder={t`Email`}
value={this.state.email}
onChange={this.handleChange} />
</Form.Item>
<Form.Item
name="password"
rules={[{ required: true }]} hasFeedback
>
<Input.Password
placeholder={t`Password`}
value={this.state.password}
onChange={this.handleChange}
/>
</Form.Item>
<Button
type="primary"
htmlType="submit"
>
<span style = {styles.button} >Sign in</span>
</Button>
</Form>
</Fragment>
</div>
)
}
}
}
I created the handle submit function and linked it to the onsubmit method inside the form and tried console logging the current state of my values inside the handle submit function. To my surprise the value from the password gets console logged for the value email too. Like this
state {email: "123", password: "123"}
I cannot figure out what am I doing wrong.
I think if you change your handleChange function to this, it should work.
handleChange = (e) => {
this.setState({ [e.target.id] : e.target.value})
}
And add id to the input fields like this
<Input id="email" placeholder={t`Email`} value={this.state.email} onChange {this.handleChange} />
<Input.Password id="password" placeholder={t`Password`} value {this.state.password} onChange={this.handleChange} />
Here is the solution:
handleChange = (e) => {
let email = ''
let password = ''
if (e.target.name === 'email') {
email = e.taget.value
} else {
password = e.taget.value
}
this.setState({ email, password})
}

Validate Material UI TextField Submit

I'm trying to validate my email and password TextField(s) for a user logging in. I'm able to catch errors via my handleSubmit function, but unsure of how to implement those errors into the MaterialUI error and helperText fields.
Note, I'm using both material-ui and react-bootstrap, that's why they're mixed.
Login.js - where the email and password TextField(s) are
import React, { Component } from 'react';
import firebase from '../firebase';
import { FiLogIn } from 'react-icons/fi';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField'
import Form from 'react-bootstrap/Form';
import Col from 'react-bootstrap/Col';
export class Login extends Component {
state = {
email : "",
password : ""
};
handleChange = (e) => {
const { id, value } = e.target
this.setState(prevState => ({
...prevState,
[id] : value
}))
};
handleSubmit = (e) => {
e.preventDefault();
const { email, password } = this.state;
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((user) => {
// User is signed in
})
.catch((error) => {
// Error
});
};
render() {
const { email, password } = this.state;
return (
<>
<Form className="sign-in-form">
<Form.Row className="align-items-center">
<Col xs="auto">
<Form.Group controlId="email">
<Form.Label srOnly>Email Address</Form.Label>
<TextField
id="email"
label="Email"
type="email"
variant="outlined"
aria-describedby="emailHelp"
placeholder="Enter email"
value={email}
onChange={this.handleChange}
/>
</Form.Group>
</Col>
<Col xs="auto">
<Form.Group controlId="password">
<Form.Label srOnly>Password</Form.Label>
<TextField
id="password"
label="Password"
variant="outlined"
type="password"
placeholder="Enter password"
value={password}
onChange={this.handleChange}
/>
</Form.Group>
</Col>
</Form.Row>
</Form>
<Button variant="contained" color="primary" className="login" type="submit" onClick={this.handleSubmit}><FiLogIn className="loginIcon" /> Login</Button>
</>
)
}
}
handleSubmit Function - where firebase validation errors are caught
handleSubmit = (e) => {
e.preventDefault();
const { email, password } = this.state;
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((user) => {
// User is signed in
})
.catch((error) => {
// Error
});
};
Let me know of what I can do here, I'm relatively new with React and always looking to learn new things.
Try this approach:
add an error state to your component:
state = {
email : "",
password : "",
error : false,
errMsg : ""
};
then change it when error is thrown from the firebase auth action inside handleSubmit:
.catch((error) => {
this.state = {error : true, errMsg: error.msg};
});
last, add a conditional TextField to show the error message:
{error && <TextField
error
id="yourErrorId"
helperText=this.state.errMsg
variant="outlined"
/>}
Make an state for error:
state = {
email : "",
password : "",
error:"",
};
Change it on catching error:
.catch((error) => {
this.setState({error: error.response.data}) // change it to your error response
});
And your input should be something like this:
<FormControl error={error}>
<InputLabel htmlFor="email">Email</InputLabel>
<Input
id="email"
value={email}
onChange={this.handleChange}
aria-describedby="email"
/>
<FormHelperText id="email"> {error ? error : "Enter your email address"}</FormHelperText>
</FormControl>
Remember to clear error state with handleChange.

Formik form no validated using Yup on test environment with jest

I'm trying to test if validation is raised when a required field is empty. In my example, I have an email input, which I set to empty, and when I simulate submit action, the onSubmit function is executed when in reality it shouldn't. I'm using validationSchema property using Yup to validate my form. I've added console.log() inside my submit function which is displayed in debug mode (and it shouldn't).
This is working in dev environment (validations raised, onSubmit function no executed) but for some reason, it doesn't work in test env.
It's worth mentioning that I'm full mounting the component to test it using Enzyme.
Thanks in advance.
I've tried with .update to check if at least the view is updated after simulating the action, but it still invokes the submit function.
Here's my code:
form.js
render() {
const { intl } = this.props;
return (
<div className="signupForm">
<Formik
initialValues={{ email: '', password: '', passwordConfirmation: '' }}
onSubmit={this.submitForm}
validationSchema={SignUpSchema}
render={ formProps => (
<Form>
<p className="signupForm__message">{formProps.errors.general}</p>
<FormControl margin="normal" fullWidth>
<Field
type="text"
name="email"
component={TextField}
className='signupForm__input'
label={intl.formatMessage(messages.email)}
/>
</FormControl>
<FormControl margin="normal" fullWidth>
<Field
type="password"
name="password"
component={TextField}
className='signupForm__input'
label={intl.formatMessage(messages.password)}
fullWidth
/>
</FormControl>
<FormControl margin="normal" fullWidth>
<Field
type="password"
name="passwordConfirmation"
component={TextField}
className='signupForm__input'
label={intl.formatMessage(messages.passConfirmation)}
fullWidth
/>
</FormControl>
<Button
type="submit"
fullWidth
variant="contained"
className='signupForm__button'
disabled={formProps.isSubmitting}
>
<FormattedMessage id="login.form.submit" />
</Button>
{formProps.isSubmitting && <Loading />}
</Form>
)
}
/>
</div>
);
}
test.js
describe('submit with blank password', () => {
beforeEach(() => {
subject = mount(withStore(<SignUpPage />, store));
// load invalid data to the form
email.simulate('change', { target: { name: 'email', value: 'joe#joe.com' } });
password.simulate('change', { target: { name: 'password', value: '' } });
passwordConfirmation.simulate('change', { target: { name: 'passwordConfirmation', value: 'password' } });
form.simulate('submit');
});
it('should display an error in the password field', () => {
subject.update();
const passwordInput = subject.find('TextField').at(1);
expect(passwordInput.props().error).toBeTruthy();
});
});
Though Formik's handleSubmit is a sync function, validation with yup functions are called asynchronously.
The following worked for me.
test("Formik validation", async () => {
const tree = mount(<YourForm />);
// Submit the form and wait for everything to resolve.
tree.simulate('submit', {
// Formik calls e.preventDefault() internally
preventDefault: () => { }
});
await new Promise(resolve => setImmediate(resolve));
tree.update();
expect(yourPasswordInput.props().error).toBeTruthy();
});

Resources