dispatching a reducer action resets form values react - reactjs

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>

Related

Submit button doesn't work in Capacitor App

I have ran into this bug, where I press the Login button, but nothing happens. In browser it works perfectly, but has some issues with capacitorJS. I have tried adding onClick function right on the button, removing Formik functionality, placing something before the try - catch. However, when using with Formik, validation actually works, it tells that the inputs can't be empty and checks email format.
This is the UI part
<form autoComplete="on" onSubmit={handleSubmit}>
<BrandTextField
name={'email'}
onChange={handleChange}
value={values.email}
placeholder={t('common:emailPlaceholder.exampleEmail')}
type={'email'}
label={t('common:email')}
className='mb-3'
error={Boolean(touched.email) && Boolean(errors.email)}
fullWidth
/>
<div>
<BrandTextField
name={'password'}
type={'password'}
onChange={handleChange}
value={values.password}
placeholder={t('common:password')}
label={t('common:password')}
className='mb-3'
error={Boolean(touched.password) && Boolean(errors.password)}
fullWidth
/>
<div
className="login-forgot-password"
onClick={() => history.push('/forgot-password')}
>
{t('signIn.signInScreen.forgotPassword')}
</div>
</div>
<div className="login-submit">
<Button type="submit" size={'auto'}>
{t('common:signIn')}
</Button>
</div>
</form>
And here is the useFormik
const {
values,
handleChange,
errors,
touched,
handleSubmit,
} = useFormik<CredentialsPayload>({
initialValues,
validationSchema,
onSubmit: async (values, { setErrors }) => {
toast.error('submit');
try {
await dispatch(actions.signIn(values));
} catch (err) {
if (err.message) {
toast.error(err.message);
}
setErrors(err.errors ?? {});
}
},
});
The issue was with the way I handled submit, It should be written onClick of button

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

react-hook-form: unregister doesnt clear component value

i have one text field which i am manually unregistering. it is successfully getting unregister and data is excluded from formdata, however user entered value still stays in the text field. i am expecting value also get cleared from component as well. i even tried
setValue('fieldName',"")
is not working. not sure if i am doing something wrong.
so if i re register my text field and trigger validation, you will see required field validation but value is still present in text field
code below:
const App = () => {
const { register, handleSubmit, unregister, errors, setValue } = useForm();
const onSubmit = (data) => {
alert(JSON.stringify(data));
};
useEffect(() => {
register("person.firstName", { required: true });
register("person.lastName", { required: true });
// }
}, [register]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>First Name</label>
<input
type="text"
name="person.firstName"
onChange={(e) => setValue("person.firstName", e.target.value)}
/>
{errors?.person?.firstName && <p> First name required</p>}
<label>Last Name</label>
<input
type="text"
name="person.lastName"
onChange={(e) => setValue("person.lastName", e.target.value)}
/>
{errors?.person?.lastName && <p> Last name required</p>}
<button
type="button"
onClick={() => {
setValue("person.lastName", "");
unregister("person.lastName");
}}
>
unregister lastName
</button>
<input type="submit" />
</form>
);
};
here is my CSB
i would appreciate any help
the error occurs because you apply your register at useEffect:
useEffect(() => {
register("person.firstName", { required: true });
register("person.lastName", { required: true });
// }
}, [register]);
instead, if you apply to ref at your input fields setValue("person.lastName", "") will clear the field as expected:
<input
ref={register({ required: true })}
type="text"
name="person.firstName"
onChange={(e) => setValue("person.firstName", e.target.value)}
/>

Submission error is not shown on react final form

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>

How to use useReducer along with Formik?

I have a file called Context.js in which I have a reducer. All of these are passed to other components using the Context API
const reducer = (state, action) => {
switch (action.type) {
case "SET_NAME":
return {...state, name: action.payload}
case "SET_LASTNAME":
return {...state, lastname: action.payload}
case "SET_EMAIL":
return {...state, email: action.payload}
default:
return state
}
In numerous other components, I am trying to use Formik to work with forms, and would like to save the form info into state, so that if the form is accessed via other components, the info that has already been provided can be found there.
<label htmlFor="name">Nome</label>
<Field maxLength="51"
name="name"
value={name}
type="text"
onChange={(e) => dispatch({ type: "SET_NAME", payload: e.target.value })}
/>
<ErrorMessage name="name" />
If I try to log the 'name', it works just fine, but as I leave the field it gives me the error message as if nothing had been typed.
I can't seem to work out how to use Formik along with useReducer or how to pass its info to other components
Formik handles forms in state so you might have to implement your form like this to help you send your input data to redux reducer: I hope this example helps
import React from "react";
import { Formik } from "formik";
import { useDispatch } from 'react-redux';
const MyForm = () => {
const dispatch = useDispatch();
return (
<Formik
initialValues={{ email: "" }}
onSubmit={async values => {
await new Promise(resolve => setTimeout(resolve, 500));
alert(JSON.stringify(values, null, 2));
}}
>
{props => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email" style={{ display: "block" }}>
Email
</label>
<input
id="email"
placeholder="Enter your email"
type="text"
value={values.email}
onChange={(e) => {
console.log(e.target.value);
// send input data to formik
handleChange(e);
// dispatch to reducer
dispatch({ type: "SET_NAME", payload: e.target.value });
}}
onBlur={handleBlur}
className={
errors.email && touched.email
? "text-input error"
: "text-input"
}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
<button
type="button"
className="outline"
onClick={handleReset}
disabled={!dirty || isSubmitting}
>
Reset
</button>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<pre
style={{
background: '#f6f8fa',
fontSize: '.65rem',
padding: '.5rem',
}}
>
<strong>props</strong> ={' '}
{JSON.stringify(formik.values, null, 2)}
</pre>
</form>
);
}}
</Formik>
)
};
export default MyForm;

Resources