Antd form doesn't identify input values - reactjs

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>

Related

useForm value not setting state react

I have a antd form where I get those values with useForm. But I not able to set my state with values from the form.
Code:
const [form] = Form.useForm();
const [userDetails, setUserDetails] = useState({});
const onFinish = () => {
console.log("form", form.getFieldValue());
setUserDetails(form.getFieldValue());
};
<Form
form={form}
name="payment"
initialValues={
{
// remember: true,
}
}
layout="vertical"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
>
<div>
<Form.Item
label="Email"
name="email"
rules={[
{
required: true,
message: "Please input your email!",
},
{
type: "email",
},
]}
>
<Input placeholder="Enter Your Email" className="text-left" />
</Form.Item>
</Form>
Console logging form.getFieldValue() is returning correct data, but couldn't set the same data to state ( setUserDetails(form.getFieldValue()); )

Why I can't fill anything in my form in React?

So I have a form, and I need users to fill this form and when they send a message, it should come to my gmail. I use EmailJS service for this.
So my form looks like this:
So as you see, users can send me messages, and my code looks like this:
Usestate for sending data:
const [toSend, setToSend] = useState({
from_name: '',
to_name: '',
message: '',
reply_to: '',
subject: ''
});
onSubmit function:
const onSubmit = (e) => {
e.preventDefault();
send(
'service_id',
'template_id',
toSend,
'user_id'
)
.then((response) => {
console.log('SUCCESS!', response.status, response.text);
})
.catch((err) => {
console.log('FAILED...', err);
});
reset();
};
handleChange function:
const handleChange = (e) => {
setToSend({ ...toSend, [e.target.name]: e.target.value });
};
useform hook:
const {register, handleSubmit, formState: { errors }, reset, trigger} = useForm();
Whole form:
<form onSubmit={handleSubmit(onSubmit)}>
<input type="text"
placeholder="Name"
name="from_name"
value={toSend.from_name}
onChange={handleChange}
id="name" {...register('name', { required: "Name is required" })}
onKeyUp={() => {
trigger("name");
}}/>
{errors.name && (<span className="danger_text">{errors.name.message}</span>)}
<input type="text"
placeholder="Email"
name="reply_to"
value={toSend.reply_to}
onChange={handleChange}
id="email" {...register("email", { required: "Email is Required" ,
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "Invalid email address",
}})}
onKeyUp={() => {
trigger("email");
}}
/>
{errors.email && (
<small className="danger_text">{errors.email.message}</small>
)}
<input type="text"
placeholder=
"Subject"
id="subj"
name="subject"
value={toSend.subject}
onChange={handleChange}/>
<input type="text"
placeholder="Message"
id="msg"
name="message"
value={toSend.message}
onChange={handleChange}
{...register('msg', { required: true })}/>
{errors.msg && (<small className="danger_text">Enter your message</small>)}
<input type="submit" className="btn_red" value="Send a message"></input>
</form>
So my problem is that I can't fill anything in inputs. When I try to type something it just doesn't type in, I'm guessing it has something to do with "value=.." in all inputs, but I'm not sure what's exactly the problem here.
You don't need to define onChnage, value, onKeyUp on your inputs, when you call register on input, it returns onChange,onBlur,ref, then react-hook-forms will control the values by ref. so below example should solve your problem:
import { useForm } from 'react-hook-form';
...
function MyComp() {
const {
register,
handleSubmit,
formState: { errors },
reset,
} = useForm();
const onSubmit = (toSend) => {
send(
'service_id',
'template_id',
toSend,
'user_id'
)
.then((response) => {
console.log('SUCCESS!', response.status, response.text);
})
.catch((err) => {
console.log('FAILED...', err);
});
reset();
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="text"
placeholder="Name"
name="from_name"
id="name"
{...register('from_name', { required: 'Name is required' })}
/>
{errors.from_name && (
<span className="danger_text">{errors.from_name.message}</span>
)}
<input
type="text"
placeholder="Email"
name="reply_to"
id="email"
{...register('reply_to', {
required: 'Email is Required',
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address',
},
})}
/>
{errors.reply_to && (
<small className="danger_text">{errors.reply_to.message}</small>
)}
<input
type="text"
placeholder="Subject"
id="subj"
name="subject"
{...register('subject')}
/>
<input
type="text"
placeholder="Message"
id="msg"
name="message"
{...register('message', { required: true })}
/>
{errors.message && <small className="danger_text">Enter your message</small>}
<input type="submit" className="btn_red" value="Send a message"></input>
</form>
);
}
BTW, consider that register works with input's name, not id.

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})
}

Trying to refactor the onSubmit property using Formik

Brushing up my development skills with React. I'm trying to figure a way to refactor the onSubmit property. My application is a contact form using the Formik component which sends the data to Firebase Cloudstore as well as sending an email via emailjs. If it's a success, it'll have a popup using Material UI's Snackbar. It works, but just trying to clean up the code. Please help!
onSubmit={(values, { resetForm, setSubmitting }) => {
emailjs.send("blah","blah", {
email: values.email,
name: values.name,
message: values.message
},
'blah',);
//this is sent to firebase cloudstore
db.collection("contactForm")
.add({
name: values.name,
email: values.email,
message: values.message,
})
.then(() => {
handleClick();
})
.catch((error) => {
alert(error.message);
});
setTimeout(() => {
resetForm();
setSubmitting(false);
/* console.log(values);
console.log(JSON.stringify(values, null, 2)); */
}, 500);
}}
Here's the complete function
function Contact() {
const [open, setOpen] = React.useState(false);
const handleClose = (event, reason) => {
if (reason === "clickaway") {
return;
}
setOpen(false);
};
const handleClick = () => {
setOpen(true);
};
const classes = useStyles();
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values, { resetForm, setSubmitting }) => {
emailjs.send("blah","blah", {
email: values.email,
name: values.name,
message: values.message
},
'blah',);
//this is sent to firebase cloudstore
db.collection("contactForm")
.add({
name: values.name,
email: values.email,
message: values.message,
})
.then(() => {
handleClick();
})
.catch((error) => {
alert(error.message);
});
setTimeout(() => {
resetForm();
setSubmitting(false);
/* console.log(values);
console.log(JSON.stringify(values, null, 2)); */
}, 500);
}}
>
{({ submitForm, isSubmitting }) => (
<Form>
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
<Alert onClose={handleClose} severity="success">
Your message has been sent!
</Alert>
</Snackbar>
<div>
<Field
component={TextField}
label="Name"
name="name"
type="name"
/>
<ErrorMessage name="name" />
</div>
<div>
<Field
component={TextField}
label="Your email"
name="email"
type="email"
/>
<ErrorMessage name="email" />
</div>
<br />
<br />
<div>
<Field
as="textarea"
placeholder="Your Message"
label="message"
name="message"
type="message"
rows="15"
cols="70"
/>
<ErrorMessage name="message" />
</div>
{isSubmitting && <LinearProgress />}
<Button
variant="contained"
color="primary"
disabled={isSubmitting}
onClick={submitForm}
>
Submit
</Button>
</Form>
)}
</Formik>
);
}
I would recommend making the onSubmit property it's own function in the component body, you will want to memoize this using useCallback. Additionally, you can create a hook to allow you to control the alert component, you can also allow the hook to control weather it's an error or success type, reducing the need to duplicate code if it fails to save.
Your submission handler could look like this, note that I omitted the sending of the email and mocked the firebase portion. Also you can call finally on the promise, rather than calling setSubmitting in both the then and catch blocks.
const handleSubmit = React.useCallback(
(values, { setSubmitting, resetForm }) => {
db.collection("contact")
.add(values)
.then((res) => {
show({ message: "Your message has been sent" });
})
.catch((err) => {
show({ variant: "error", message: "Failed to send your message." });
})
.finally(() => {
setSubmitting(false);
});
},
[show]
);
The show function in the above example would be part of your hook to control the alert. The hook could look something like this, it could be extended based on your usecase.
import React from "react";
const useAlert = () => {
const [state, setState] = React.useState({
variant: "success",
visibile: false,
message: null
});
const show = React.useCallback(
(options = {}) => {
setState((prev) => ({
...prev,
...options,
visible: true
}));
},
[setState]
);
const hide = React.useCallback(() => {
setState((prev) => ({ ...prev, visibile: false }));
}, [setState]);
return { ...state, show, hide };
};
export default useAlert;
Additionally, since you're using material ui, you'll want to take advantage of their built in components. This would remove the need for your multiple <br />s for spacing, as well as help to keep the UI consistent.
<Box marginBottom={1}>
<Field component={TextField} label="Name" name="name" type="name" />
<ErrorMessage name="name" />
</Box>
<Box marginBottom={1}>
<Field
component={TextField}
label="Email"
name="email"
type="email"
/>
<ErrorMessage name="email" />
</Box>
Also, you could use the built in component for the text area, keeping the design consistent. Using the multiline prop allows you to make the input a text area.
<Box marginBottom={2}>
<Field
component={TextField}
placeholder="Your Message"
label="Message"
name="message"
type="message"
rows={5}
multiline
fullWidth
/>
<ErrorMessage name="message" />
</Box>
I'm personally not a huge fan of using the LinearProgress in the manner than your did. I personally think that the circular process looks better, specifically when used inside the submit button. Here are the relevant docs.
<Button
variant="contained"
color="primary"
disabled={isSubmitting}
onClick={submitForm}
endIcon={isSubmitting && <CircularProgress size={15} />}
>
Submit
</Button>
I've put a working example together in a codesandbox.

Login Form validation in Reactjs : material ui <textfield>

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.

Resources