I'm working to enable server-side validation with redux-form, where when a user submits the SignUp form, if the server responds with an error, the form shows the error... In this case the error has a status of 422 from the server and is responding w {"errors":{"email":["has already been taken"]}}
While the form is submitting fine, I can't get the SubmissionError to display on the form. I'm getting the following error in the JS console:
Unhandled rejection SubmissionError: Submit Validation Failed
What am I doing wrong with redux-form submit validation handling? Thanks
SignUpForm
const ensureValid = response => {
if (response.status === 422) {
return response.json().then(({errors}) => {
throw new SubmissionError({
email: 'Already in Use',
_error: 'failed!',
});
});
}
return response;
};
class SignUp extends React.Component {
handleSubmit(data) {
const request = new Request('http://localhost:4400/auth/', {
method: 'POST',
headers: new Headers({
'Accept' : 'application/json',
'Content-Type' : 'application/json',
}),
body: JSON.stringify({ user: data })
});
fetch(request).then(ensureValid).then(response => {
return tryLogin(response, this.props.dispatch);
});
}
render() {
const { error, handleSubmit, pristine, reset, submitting } = this.props;
return (
<div className="container">
<div className="row justify-content-md-center">
<div className="col-6">
<h1>Sign Up - it's free.</h1>
<form onSubmit={this.props.handleSubmit(this.handleSubmit.bind(this))}>
<Field
name="first_name"
type="text"
component={renderField}
label="First Name"
/>
<Field
name="last_name"
type="text"
component={renderField}
label="Last Name"
/>
<Field
name="email"
type="text"
component={renderField}
label="Email"
/>
<Field
name="password"
type="password"
component={renderField}
label="Password"
/>
<button type="submit" disabled={submitting}>Submit</button>
{error && <strong>{error}</strong>}
</form>
</div>
</div>
</div>
);
}
}
// Decorate the form component
SignUp = reduxForm({
form: 'SignUp' // a unique name for this form
})(SignUp);
const mapStateToProps = state => {
return {
};
};
const mapDispatchToProps = (dispatch) => bindActionCreators({
dispatch
}, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(SignUp);
Turns out I needed to update:
BEFORE:
fetch(request)...
AFTER:
return fetch(request) ...
Related
I am working on an address book and I have fetched all the data from this API url:
https://jsonplaceholder.typicode.com/users
The API cannot really be modified, but it should behave "like if" according to this message in the documentation: "resource will not be really updated on the server but it will be faked as if."
I have set up the react hook form but when I submit my form this is what I get in the dev tools tab network? Shouldn't be showing the user inputs at this point instead of empty string for all fields? The id is the only thing that gets updated.
Is there anything wrong with my Submit function or the actual fetch? Is it ok I have the POST fetch in this component where I have my form as well or should be in the same component where I have the GET request?
Would this one be a good way to approach the POST request?
const NewUserForm = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = () => {
fetch(URL, {
method: 'POST',
body: JSON.stringify({
id:'',
name: '',
email: '',
address1:'',
address2:'',
city:'',
phone:''
}),
headers: {
'Content-type': 'application/json; charset=UTF-8'
},
})
.then((response) => response.json())
.then((json) => console.log(json));
}
return (
<>
<Header>New user</Header>
<FormContainer>
<Form onSubmit={handleSubmit(onSubmit)}>
<input type="text" placeholder="name" {...register("name", { required: true })} />
{errors.name && <span>This field is required</span>}
<input type="text" placeholder="email" {...register("email", { required: true })} />
{errors.email && <span>This field is required</span>}
<input type="text" placeholder="address1"{...register("address1", { required: true })} />
{errors.address1 && <span>This field is required</span>}
<input type="text" placeholder="address2"{...register("address2", { required: true })} />
{errors.address2 && <span>This field is required</span>}
<input type="text" placeholder="city"{...register("city", { required: true })} />
{errors.city && <span>This field is required</span>}
<input type="text" placeholder="phone"{...register("phone", { required: true })} />
{errors.phone && <span>This field is required</span>}
<input type="submit" />
</Form>
</FormContainer>
</>
);
}
Okay, after checking the react-hook-form docs, here is a possible solution:
In the docs, it says that your onSubmit will have a data param:
const onSubmit = (data) => alert(JSON.stringify(data));
Which means that you can use that in your onSubmit too.
Try changing your onSubmit to use the data parameter:
const onSubmit = (data) => {
fetch(URL, {
method: 'POST',
body: JSON.stringify(data),
And revert the change I suggested earlier regarding handleSubmit. This is correct:
<Form onSubmit={handleSubmit(onSubmit)}>
I am trying to send data to contact form 7 using Formik but i get the error of fields that are empty. Can you please guide what is the problem with it. If i remove required fields from contact form 7, the form send an email with empty fields.
import * as React from 'react';
import * as Yup from 'yup';
import axios from 'axios';
import { Formik } from 'formik';
const URL = 'YOUR SITE URL';
const CF7_ID = 'YOUR CONTACT FORM ID';
const formSchema = Yup.object().shape({
formName: Yup.string().required('Required'),
formEmail: Yup.string()
.email('Invalid email')
.required('Required'),
});
/*
function convertJsontoUrlencoded(obj) {
let str = [];
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
}
}
return str.join('&');
}
*/
const CF7 = () => {
const [state, setState] = React.useState(null || '');
return (
<>
<Formik
initialValues={{
formSubject: 'Your message',
formName: 'SAM ',
formEmail: 'salman#gmail.com',
formMessage: 'thisisisibdiad iuasdhasiud aidsuahdsiuahsd',
}}
validationSchema={formSchema}
onSubmit={(values, { setSubmitting }) => {
const submitData = async () => {
try {
const result = await axios({
//body: JSON.stringify(values),
url: `${URL}/wp-json/contact-form-7/v1/contact-forms/${CF7_ID}/feedback`,
headers: {
//Authorization: `Basic ${TOKEN}`,
'content-Type': 'application/json; charset=UTF-8',
//'content-type': 'multipart/form-data'
},
method: 'POST',
data: JSON.stringify(values),
});
setState(result.data.message);
setSubmitting(false);
} catch (error) {
setState('Something Went Wrong');
}
};
submitData();
}}
>
{({ values, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting }) => (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="">
Subject
<input
type="text"
name="formSubject"
onChange={handleChange}
onBlur={handleBlur}
value={values.formSubject}
/>
{errors.formSubject && touched.formSubject ? <div>{errors.formSubject}</div> : null}
</label>
</div>
<div>
<label htmlFor="">
Name *
<input
type="text"
name="formName"
onChange={handleChange}
onBlur={handleBlur}
value={values.formName}
/>
{errors.formName && touched.formName ? <div>{errors.formName}</div> : null}
</label>
</div>
<div>
<label htmlFor="">
Email *
<input
type="email"
name="formEmail"
onChange={handleChange}
onBlur={handleBlur}
value={values.formEmail}
/>
{errors.formEmail && touched.formEmail ? <div>{errors.formEmail}</div> : null}
</label>
</div>
<div>
<label htmlFor="">
Message
<input
type="text"
name="formMessage"
onChange={handleChange}
onBlur={handleBlur}
value={values.formMessage}
/>
{errors.formMessage && touched.formMessage ? <div>{errors.formMessage}</div> : null}
</label>
</div>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</form>
)}
</Formik>
{state ? <p>{state}</p> : null}
</>
);
};
export default CF7;
ERROR:
into: "#"
invalid_fields: [{into: "span.wpcf7-form-control-wrap.formName", message: "The field is required.", idref: null,…},…]
message: "One or more fields have an error. Please check and try again."
posted_data_hash: ""
status: "validation_failed"
I'm having a hard time figuring out how to handle errors that don't necessarily pertain to a single input field in a react-hook-form.
To put it differently, how do I handle handleSubmit errors?
For example, having the following form:
import to from 'await-to-js'
import axios, { AxiosResponse } from 'axios'
import React from "react"
import { useForm } from "react-hook-form"
type LoginFormData = {
username: string,
password: string,
}
export const Login: React.FC = () => {
const { register, handleSubmit } = useForm<LoginFormData>()
const onSubmit = handleSubmit(async (data) => {
const url = '/auth/local'
const [err, userLoginResult] = await to<AxiosResponse>(axios.post(url, data))
if (userLoginResult) {
alert('Login successful')
}
else if (err) {
alert('Bad username or password')
}
})
return (
<div className="RegisterOrLogIn">
<form onSubmit={onSubmit}>
<div>
<label htmlFor="username">username</label>
<input name="username" id="username" ref={register} />
</div>
<div>
<label htmlFor="password">Password</label>
<input type="password" id="password" name="password" ref={register} />
</div>
<button type="submit"> </button>
</form>
</div>
)
}
Is there a react-hook-form way of informing the user that there's an error with either the username or the password?
as in, other than alert()
Perhaps this is answered elsewhere, but I could not find it.
Clarification
The error received from the server does not pertain to a single field:
{
"statusCode": 400,
"error": "Bad Request",
"message": [
{
"messages": [
{
"id": "Auth.form.error.invalid",
"message": "Identifier or password invalid."
}
]
}
],
"data": [
{
"messages": [
{
"id": "Auth.form.error.invalid",
"message": "Identifier or password invalid."
}
]
}
]
}
In order to display the error from the server to your user, you need to use:
setError to set the error programmatically when the server returns an error response.
errors to get the error state of every fields in your form to display to the user.
type FormInputs = {
username: string;
};
const { setError, formState: { errors } } = useForm<FormInputs>();
In your handleSubmit callback
axios
.post(url, data)
.then((response) => {
alert("Login successful");
})
.catch((e) => {
const errors = e.response.data;
if (errors.username) {
setError('username', {
type: "server",
message: 'Something went wrong with username',
});
}
if (errors.password) {
setError('password', {
type: "server",
message: 'Something went wrong with password',
});
}
});
In your component
<label htmlFor="username">username</label>
<input id="username" {...register("username")} />
<div>{errors.username && errors.username.message}</div>
Live Demo
Inspired by #NearHuscarl's answer, I've done the following hack s.t. changes in either the username or the password inputs will remove the single error.
This hack does not scale well if your error is related to multiple fields in the form, but it worked for the login use case.
onSubmit:
const onSubmit = handleSubmit(async (data) => {
const url = '/auth/local'
const [err, userLoginResult] = await to<AxiosResponse>(axios.post(url, data)) // see await-to-js
if (userLoginResult) {
alert('Login successful')
}
else if (err) {
const formError = { type: "server", message: "Username or Password Incorrect" }
// set same error in both:
setError('password', formError)
setError('username', formError)
}
})
component:
return (
<div className="RegisterOrLogIn">
<form onSubmit={onSubmit}>
<div>
<label htmlFor="username">username</label>
<input name="username" id="username" ref={register} />
</div>
<div>
<label htmlFor="password">Password</label>
<input type="password" id="password" name="password" ref={register} />
</div>
<div>{errors.username && errors.password?.message /*note the cross check*/}</div>
<button type="submit"> </button>
</form>
</div>
)
by setting and rendering the error on both errors.password & errors.username, the error will disappear when the user updates either of those fields.
I have React login form. The problem is form submit is called two times. I dont understand it. Can somebody tell me please why? Thanks a lot.
import React from "react"
import LoginErrors from "./LoginErrors"
import LoginSuccess from "./LoginSuccess"
import axios from 'axios'
import { withRouter } from "react-router-dom";
class Login extends React.Component
{
constructor(props)
{
super(props)
this.state = {
name: '',
password: '',
errors: [],
success: []
}
}
changeName = e =>
{
this.setState({name: e.target.value});
}
changePassword = e =>
{
this.setState({password: e.target.value});
}
sendData(e)
{
e.preventDefault();
this.setState({'errors': [], 'success': []});
let formData = new FormData();
formData.set('name', this.state.name);
formData.set('password', this.state.password);
axios({
method: 'POST',
url: 'http://localhost:8000/login',
data: formData,
headers: {
'Content-Type': 'text/html',
'X-Requested-With': 'XMLHttpRequest',
}
})
.then(response => {
// ? returns undefined if variable is undefined
if( response.data?.errors?.length )
{
this.setState({errors: response.data.errors})
}
if( response.data?.success?.length )
{
let data = response.data
this.props.setAccessToken(data.token)
this.props.setUserName(data.user.name)
this.props.history.push('/')
}
})
.catch(response => {
this.setState({errors: ['Login fails. Try it again later please.']})
});
}
render() {
return (
<div className="row justify-content-md-center">
<div className="col-sm-12 col-md-6">
<div id="loginForm">
<h2 className="">Login</h2>
<LoginSuccess success={this.state.success} />
<LoginErrors errors={this.state.errors} sendParentMessage={this.handleErrorsChildMessage} />
<form onSubmit={e => {this.sendData(e)}}>
<div className="form-group">
<label htmlFor="name">Name</label>
<input ref={this.email} name="name" className="form-control" type="text" onChange={this.changeName} />
</div>
<div className="form-group">
<label htmlFor="password">Heslo</label>
<input ref={this.password} name="password" className="form-control" type="password" onChange={this.changePassword} />
</div>
<div className="form-group">
<button name="sbmt" className="btn btn-primary" type="submit">Odoslať</button>
</div>
</form>
</div>
</div>
</div>
);
}
}
So the problem is in axios preflyght request which is related to the CORS policy. But How to stop it?
I made a login form with Redux Form and when I submit the form I make a POST request to validate the Email and Password. If the combination is incorrect I throw a new SubmissionError but this SubmissionError gets removed immediately by an UPDATE_SYNC_ERRORS call by Redux Form.
My LoginForm component:
const LoginForm = ({ error, handleSubmit, authenticateUser }) => (
<form onSubmit={handleSubmit(authenticateUser)}>
<Field
name="email"
component={RenderField}
type="email"
label="email"
validate={[required, minLength(3), email]}
/>
<Field
name="password"
component={RenderField}
type="password"
label="password"
validate={[required]}
/>
{error && <strong>{error}</strong>}
<div className="login__action__container">
<button className="login__submit" type="submit">Submit</button>
<NavLink className="login__link" to="/register">Or register</NavLink>
</div>
</form>
)
The AuthenticateUser action:
export const authenticateUser = values => dispatch => {
return axios.post('./api/user/login', {
email: values.email,
password: values.password
})
.then((response) => {
// Does stuff
})
.catch((error) => {
throw new SubmissionError({_error: error.response.data.error})
})
Image showcasing the Redux State and actions
I've been bug fixing for the last few hours but still no result. Anyone with the golden tip? Thanks in advance!