Submission error is not shown on react final form - reactjs

I'm having troubles with React-Final-Form library. From the onSubmit function, I return a validation error, but it is not displayed on the form itself. Here you can see my code:
My form:
<Form
onSubmit={this.onSubmit}
initialValues={{}}
validate={
values => {
const errors = {}
if (!values.username) {
errors.username = "This field is required"
}
if (!values.password) {
errors.password = "This field is required"
}
return errors
}
}
render={({submitError, handleSubmit, submitting, values}) => (
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<Field name="username">
{({input, meta}) => (
<>
<input {...input} type="text" placeholder="Username"
className={"form-control" + ((meta.error || meta.submitError) && meta.touched ? " is-invalid" : "")}/>
{(meta.error || meta.submitError) && meta.touched ?
<div
className="invalid-feedback">{meta.error || meta.submitError}</div> : null
}
</>
)}
</Field>
{submitError && (
<div>{submitError}</div>
)}
</div>
<div className="form-group">
<label>Password</label>
<Field name="password">
{({input, meta}) => (
<>
<input {...input} type="password"
placeholder="Password"
className={"form-control" + ((meta.error || meta.submitError) && meta.touched ? " is-invalid" : "")}/>
{(meta.error || meta.submitError) && meta.touched ?
<div
className="invalid-feedback">{meta.error || meta.submitError}</div> : null
}
</>
)}
</Field>
</div>
<button type="submit" disabled={submitting}
className="btn btn-block btn-outline-secondary">Log In
</button>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
My onSubmit function:
onSubmit = async values => {
const errors = {}
let loginData = {
username: values.username,
password: values.password
}
usersAPI.getCSRF().then((response) => {
usersAPI.login(loginData)
.then(response => {
if (response.data.login_status === "Incorrect data") {
console.log("Invalid data")
errors.username = "Invalid login or password"
errors.password = "Invalid login or password"
return errors
} else {
localStorage.setItem('token', response.data.token)
this.props.setAuthState(true)
console.log("Logged in successfully")
}
})
.catch(err => console.log(err))
})
}
The problem is in axios request. If my back-end server sends the response that credentials are invalid, then console.log("Invalid data") will appear in console but nothing will happen with form. If I comment my axios request and just leave this code:
onSubmit = async values => {
return {username: "Invalid data"}
}
So, in this case everything works correct. Who can tell me How can I optimize my axios request so after that my form will get submission errors that returned from
errors.username = "Invalid login or password"
errors.password = "Invalid login or password"
return errors

I found mistake. Mistake was in my return function. It just returns this value to usersAPI.getCSRF() function, not to global onSubmit function. I made some changes so my final code looks like this:
onSubmit = values => {
let loginData = {
username: values.username,
password: values.password
}
let success = false;
usersAPI.getCSRF().then((response) => {
usersAPI.login(loginData)
.then(response => {
if (response.data.login_status !== "Incorrect data") {
success = true
localStorage.setItem('token', response.data.token)
}
})
.catch(err => console.log(err))
})
if (success) this.props.setAuthState(true)
else return {username: "Incorrect username or password", password: "Incorrect username or password"}
}

I spent 2 days on the same issue (submission errors not displaying) and wanted to post my solution here in case anyone else ran into my issue because I could not find it online.
Basically I have a form set up like so:
const onSubmit = (values) => {
...
return {username: 'required'} // errors object
}
<Form
onSubmit={onSubmit}
>
{({values}) => (
<form>
<a onClick={() => onSubmit(values)}>save</a>
...restOfForm
</form>
)}
</Form>
The issue above is that I am calling my "onSubmit" function directly, so the returned submission errors go nowhere. I needed to call the "handleSubmit" function passed into the render function to populate the errors like so:
<Form
onSubmit={onSubmit}
>
{({values, handleSubmit}) => (
<form>
<a onClick={() => handleSubmit(values)}>save</a>
...restOfForm
</form>
)}
</Form>

Related

how to use multiple ref in my react input

hello guys pls i have a challenge in my react app, i am trying to enable multiple choice login where a user can either use his mail or even hi username to login. i got it working from my server using postman but when i try it on my frontend it doesnt allow me to use multiple refs on it, i am only allowed to use either the username or email.
Here is my code
function Login() {
const password = useRef();
const email = useRef();
const username = useRef();
const { isFetching, dispatch } = useContext(AuthContext);
const handleClick = (e) => {
e.preventDefault();
try {
loginCall(
{
username: username.current.value,
email: email.current.value,
password: password.current.value,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
return (
<>
<div className="log__bg">
<div className="login">
<div className="loginWrapper">
<div className="loginLeft">
<span className="loginDesc">Welcome Back</span>
<ToastContainer />
</div>
<div className="loginRight">
<form className="loginBox" onSubmit={handleClick} >
<input
placeholder="Email, phone, or username"
type="text"
required
className="loginInput"
ref={username}
/>
<input
placeholder="Password"
type="password"
required
minLength="6"
className="loginInput"
ref={password}
/>
<button
className="loginButton"
type="submit"
disabled={isFetching}
>
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Log In"
)}
</button>
<Link id="auth_route" to="/register">
<button className="loginRegButton">
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Create a New Account"
)}
</button>
</Link>
<div className="forgot__password">
<Link className="forgot__password__btn" to="/forgot-password">
Forgot Password?
</Link>
</div>
</form>
</div>
</div>
</div>
</div>
</>
);
}
export default Login;
Why you need 2 refs ? user will provide username or email in the same input field. Use just one ref than you can have a logic or regex in your backend to detect if the provided value is an email or a username:
const handleClick = (e) => {
e.preventDefault();
let username = null
let email = null
const emailRegex = /^\S+#\S+\.\S+$/
const inputValue = username.current.value
if(inputValue.match(emailRegex)){
email = inputValue
} else {
username = inputValue
}
try {
loginCall(
{
username: username,
email: email,
password: password.current.value,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
With this if the user provided an email, username will be sent as null to the server and vice versa. Then you can check on the server if you get an email or a username.
UPDATE:
You can also just use a state and don't use ref at all:
const [usernameValue, setUsernameValue]=React.useState("")
const [passwordValue, setPasswordValue]=React.useState("")
const handleClick = (e) => {
e.preventDefault();
let username = null
let email = null
const emailRegex = /^\S+#\S+\.\S+$/
if(usernameValue.match(emailRegex)){
email = inputValue
} else {
username = inputValue
}
try {
loginCall(
{
username: username,
email: email,
password: passwordValue,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
return (
...
<form className="loginBox" onSubmit={handleClick} >
<input
placeholder="Email, phone, or username"
type="text"
required
className="loginInput"
value={usernameValue}
onChange={e=>setUsernameValue(e.target.value)}
/>
<input
placeholder="Password"
type="password"
required
minLength="6"
className="loginInput"
value={passwordValue}
onChange={e=>setPasswordValue(e.target.value)}
/>
...
...
)
I agree with #PatersonCode, the best way would be to use React.useState and just one variable to store your input value.
You didn't mention what error you're getting, but here's an example that I hope might help you in some way:
https://codesandbox.io/s/choose-login-method-example-xgpfue?file=/src/Login.js
thanks to everyone that helped, i have solved it and the problem was that i needed to use one ref (email or username) and also that same ref in my loginCall. here is the updated code.
function Login() {
const password = useRef();
const username = useRef();
const { isFetching, dispatch } = useContext(AuthContext);
const handleClick = (e) => {
e.preventDefault();
try {
loginCall(
{
username: username.current.value,
email: username.current.value,
password: password.current.value,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
return (
<>
<div className="log__bg">
<div className="login">
<div className="loginWrapper">
<div className="loginLeft">
<span className="loginDesc">Welcome Back</span>
<ToastContainer />
</div>
<div className="loginRight">
<form className="loginBox" onSubmit={handleClick} >
<input
placeholder="Email, phone, or username"
type="text"
required
className="loginInput"
ref={username}
/>
<input
placeholder="Password"
type="password"
required
minLength="6"
className="loginInput"
ref={password}
/>
<button
className="loginButton"
type="submit"
disabled={isFetching}
>
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Log In"
)}
</button>
<Link id="auth_route" to="/register">
<button className="loginRegButton">
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Create a New Account"
)}
</button>
</Link>
<div className="forgot__password">
<Link className="forgot__password__btn" to="/forgot-password">
Forgot Password?
</Link>
</div>
</form>
</div>
</div>
</div>
</div>
</>
);
}
export default Login;

dispatching a reducer action resets form values react

I have forgot password page which obviously takes email input. After submitting the form I am displaying a toast notification confirming the success or error of operation.
I created a Notification components which is rendered when ever my application state has the object of notification filled.
By submitting the form I am then dispatching an action which fills the notification object.
It works fine
But after dispatch the form loses its fields values
const {control, handleSubmit, setError, formState: {errors}, isSubmitting} = useForm({defaultValues: {email: ''}})
This is my form submit handler
const onSubmit = async data => {
if (Object.values(data).every(field => field.length > 0)) {
useJwt
.forgotPassword({email: data.email})
.then(res => dispatch(toastNotify({ status: "success", ...res.message })))
.catch(({response}) => {
if (response.status === 400 && response.data.hasOwnProperty('errors')) {
response.data.errors.forEach(error => setError(error['param'], {type: 'manual', message: error['msg']}))
} else {
// If I comment line below and log the response data, form values works fine
dispatch(toastNotify({type: "error", ...response.data}))
}
})
} else {
for (const key in data) {
if (data[key].length === 0) {
setError(key, {
type: 'manual'
})
}
}
}
}
This is my form
<Form className='auth-forgot-password-form mt-2' onSubmit={handleSubmit(onSubmit)}>
<div className='mb-2'>
<Label className='form-label' for='email'>
Email
</Label>
<Controller
id='email'
name='email'
control={control}
render={({field}) => (
<Input type='text' id='email' name='email' autoFocus invalid={errors.email && true} {...field} />
)}
/>
{errors && errors.email && <FormFeedback>{ errors.email.message }</FormFeedback>}
</div>
<Button color='primary' disabled={isSubmitting} block>
{isSubmitting ? <Spinner size='sm' /> : 'Send reset link'}
</Button>
</Form>

Why won't the form state visibly change within the submit button using react form hook?

So I have a sign up form using react-hook-form and I want to make the submit input disabled and display a "Signing in..." message. I've console logged the isSubmitting value within the render and that shows true when I submit and then false not long after however the submit button within the form never updates to reflect the isSubmitting status.
What am I doing wrong? Here is the React Hook Form useFormState docs
From what I can see it should work?
Thanks in advance.
import { useState } from "react"
import { useForm, useFormState } from "react-hook-form"
import useAuth from "Hooks/useAuth"
const SignInForm = () => {
const [firebaseError, setFirebaseError] = useState(null)
const { signIn } = useAuth()
const {
register,
handleSubmit,
resetField,
control,
formState: { errors },
} = useForm()
const { isSubmitting, isValidating } = useFormState({ control })
const onSubmit = (data) => {
signIn(data.email, data.password)
.then((response) => console.log(response))
.catch((error) => {
let message = null
if (error.code === "auth/too-many-requests") {
message =
"Too many unsuccessful attempts, please reset password or try again later"
}
if (error.code === "auth/wrong-password") {
message = "Incorrect password, please try again"
}
if (error.code === "auth/user-not-found") {
message = "User does not exist, please try again"
}
resetField("password")
setFirebaseError(message)
})
}
return (
<form
className="signupForm"
onSubmit={handleSubmit(onSubmit)}
autoComplete="off"
>
{console.log(isSubmitting)}
{firebaseError && (
<p className="form-top-error has-text-danger">{firebaseError}</p>
)}
<div className="field">
<input
type="text"
className="input formInput"
placeholder="Email"
{...register("email", {
required: {
value: true,
message: "Field can not be empty",
},
pattern: {
value:
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
message: "Invalid email",
},
})}
/>
{errors.email && (
<span className="is-block has-text-danger is-size-7">
{errors.email?.message}
</span>
)}
</div>
<div className="field">
<input
type="password"
className="input formInput"
placeholder="Password"
{...register("password", {
required: "Field can not be empty",
minLength: {
value: 6,
message: "Must be longer than 6 characters",
},
})}
/>
{errors.password && (
<span className="is-block has-text-danger is-size-7">
{errors.password?.message}
</span>
)}
</div>
<input
type="submit"
className="button is-info"
value={isSubmitting ? "Signing In..." : "Sign In"}
disabled={isSubmitting}
/>
</form>
)
}
export default SignInForm
I think you need to refactor your onSubmit function to make it async so isSubmitting will stay true during your signIn call.
const onSubmit = async (data) => {
await signIn(data.email, data.password)
.then((response) => console.log(response))
.catch((error) => {
let message = null
if (error.code === "auth/too-many-requests") {
message =
"Too many unsuccessful attempts, please reset password or try again later"
}
if (error.code === "auth/wrong-password") {
message = "Incorrect password, please try again"
}
if (error.code === "auth/user-not-found") {
message = "User does not exist, please try again"
}
resetField("password")
setFirebaseError(message)
})
}
onSubmit needs to return a Promise for formState to update correctly.
const onSubmit = (payload) => {
// You need to return a promise.
return new Promise((resolve) => {
setTimeout(() => resolve(), 1000);
});
};
References:
https://react-hook-form.com/api/useform/formstate/
https://github.com/react-hook-form/react-hook-form/issues/1363#issuecomment-610681167

Formik axios post data to contact form 7 , empty fields

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"

How to validate email and password using react hooks?

I am getting state values while clicking submit button but I am unable to do the validation for my login form and how to display the error messages below the input field when I enter my input wrong or empty. please give me a solution to this.Thanks in advance.
const Login = () => {
const [state, setState] = useState({
email: "",
password: ""
});
const handleChange = (e) => {
const {id, value} = e.target
setState(prevState => ({
...prevState,
[id]: value
}))
}
const handleSubmitClick = (e) => {
e.preventDefault();
console.log("Authenticated",state);
}
return(
<>
<div className="container">
<div className="title">
<form onSubmit={handleSubmitClick}>
<div className="form-group">
<input
type="email"
className="email"
placeholder="Email"
value={state.email}
onChange={handleChange}/>
</div>
<div className="form-group">
<input
type="password"
className="password"
placeholder="Password"
value={state.password}
onChange={handleChange}/>
</div>
<button type="submit" className="button">Enter</button>
</form>
</div>
</div>
</>
)
}
export default Login;
If you want to perform client-side validation, you can create hook like this:
const useEmailValidation = (email) => {
const isEmailValid = /#/.test(email); // use any validator you want
return isEmailValid;
};
And then you can use this hook in your form component:
...
const isEmailValid = useEmailValidation(state.email);
const isPasswordValid = usePasswordValidation(state.password);
const isFormValid = isEmailValid && isPasswordValid;
return (
...
<input
className={classNames({ 'invalid': !isEmailValid })}
type="email"
value={state.email}
onChange={handleChange}
/>
{!isEmailValid && 'Some error message'}
<button type="submit" disabled={!isFormValid} className="button">Enter</button>
...
);
...
Your validator hook can return validation message instead of boolean, like:
const useEmailValidation = (email) => {
if (!email || email.length === 0) {
return 'Email cannot be empty';
}
const isEmailValid = /#/.test(email); // use any validator you want
if (!isEmailValid) {
return 'Invalid email provided';
}
return null;
};
Also it is a good practice to show validation message only after field was focused before and after user tried to submit the form.
Formik is a great plugin that will help you perform form validation. The examples are also quite clear.
Or you could do something like this:
const Login = () => {
const [error, setError] = useState(null);
const [state, setState] = useState({
email: '',
password: '',
});
const validateEmail = (email) => {
const re =
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
};
const handleChange = (e) => {
const { id, value } = e.target;
setState((prevState) => ({
...prevState,
[id]: value,
}));
};
const handleSubmitClick = (e) => {
e.preventDefault();
if (!validateEmail(state.email)) {
setError('Invalid Email');
}
if (state.password.length < 8) {
setError('Password must be at least 8 chars long');
}
if (!error) {
// No errors.
}
};
return (
<>
<div className='container'>
<div className='title'>
{error && <div style={{ color: 'red' }}>{error}</div>}
<form onSubmit={handleSubmitClick}>
<div className='form-group'>
<input
type='email'
className='email'
placeholder='Email'
value={state.email}
onChange={handleChange}
/>
</div>
<div className='form-group'>
<input
type='password'
className='password'
placeholder='Password'
value={state.password}
onChange={handleChange}
/>
</div>
<button type='submit' className='button'>
Enter
</button>
</form>
</div>
</div>
</>
);
};
export default Login;
For an empty validation you can check it preventing the submit if the field is empty, like
const handleSubmitClick = (e) => {
e.preventDefault();
if(email.trim() === '' || password.trim() === ''){
//Add a h1 or section with the error message
}else{
console.log("Authenticated",state);
}
}
As long as the email field type is equal to email, which is your case, the browser should give an alert if the string is not an email. ("user#example.com")

Resources