Automatically trim white spaces with Yup and Formik - reactjs

I am using a Formik React Form and Yup validation defined on a schema:
export const Contact = yup.object<IContact>().shape({
contactName: yup
.string()
.trim('The contact name cannot include leading and trailing spaces')
.strict(true)
.min(1, 'The contact name needs to be at least 1 char')
.max(512, 'The contact name cannot exceed 512 char')
.required('The contact Name is required'),
});
Is there a way to have Yup trim white spaces without showing a message? So automatically trimming the spaces when a form is submitted?

Is there a way to have Yup trim white spaces without showing a message
Not in a single transform. The yup transform used by formik is only for validation.
You can create a seperate transform to use before passing the data, but its simpler to just valueToUse = userValue.trim() yourself.

You can do:
onSubmit={(values) => {
const castValues = Contact.cast(values)
})
reference:
https://github.com/jaredpalmer/formik/issues/473#issuecomment-499476056

I was able to achieve automatic removal of white spaces in an email field by overriding the onChange method from formik object. This is not the best solution to this but it works fine, especially where spaces are not allowed anywhere in the input field.
const { errors, touched, getFieldProps } = formik;
const { onChange } = getFieldProps('email');
const emailFieldProps = {
...getFieldProps('email'),
onChange: (e) => {
e.target.value = e.target.value.trim();
onChange(e);
},
};
return (
...
<TextField
{...emailFieldProps}
error={Boolean(touched.email && errors.email)}
helperText={touched.email && errors.email}
/>
...
)

You can also trim entered text on the go to completely limit user from using spaces at the end (example uses react native components):
const {handleSubmit, values, errors} =
useFormik({
validationSchema: EmailSchema(),
initialValues: {email: ''},
onSubmit: values => tryLogin(values.email)
})
...
<TextInput
value={values.email}
onChangeText={text => handleChange('email')(text.trim())}
error={errors.email}
/>
<Button title='SUBMIT' onPress={handleSubmit} />

Related

React using Formik does not clear the data value in Material UI form

I'm using formik.resetForm() to remove values from text fields in a form after submitting the data.
...
const handleSubmitProduct = async (values: Object, resetForm: any) => {
... code to handle my form data ...
resetForm()
if (response.ok) {
console.debug(response.status)
} else {
console.error(response)
}
}
const validate = (values: Object) => {
const errors: any = {}
if (!values.product_name) {
errors.product_name = "Include name"
}
return errors
}
... initialValues defined ...
const formik = useFormik({
initialValues: initialValues,
validate,
onSubmit: (values: Object, { resetForm }) => {
console.debug(JSON.stringify(values, null, 2))
handleSubmitProduct(values, resetForm)
},
})
return (
<FormLabel>Display name</FormLabel>
<TextField
onChange={formik.handleChange}
id="product_name"
onBlur={formik.handleBlur}
error={formik.touched.product_name && Boolean(formik.errors.product_name)}
helperText={formik.touched.product_name && formik.errors.product_name}
/>
<Button onClick={() => formik.handleSubmit()} variant="contained">
Submit
</Button>
)
I know there are many other questions like this but mine is different where I know the underlying Formik resources for values, errors, touched have been cleared but the values are still present in the text boxes.
The issue is I know the underlying Formik objects are cleared because after I submit, the validation triggers and prompts me like there is no value in the text field.
I've tried
resetForm({values: {initialValues}}) has the same result
resetForm(initialValues) has the same result
Use action.resetForm({values: {initialValues}}) in the onSubmit() which same result
https://codesandbox.io/s/mui-formik-fr93hm?file=/src/MyComponent.js but this approach uses the <Formik /> as opposed to useFormik which would change up my entire page but I'm in process to try anyway
I think the problem is that value of TextField is not value of formik. so the TextField is not controlled and by chaning value of formik it won't change.
assigning value of formik to it will do what you want
value={formik.values.firstName}
like this :
<TextField
onChange={formik.handleChange}
id="product_name"
value={formik.values.firstName}
onBlur={formik.handleBlur}
error={formik.touched.product_name && Boolean(formik.errors.product_name)}
helperText={formik.touched.product_name && formik.errors.product_name}
/>

Formik values didnt update after change

I cant get the values from disabled input after fetch data. I think cause i put 2 condition in my input value component like this. is there a way to run properly from this code. i use formik to handle my form
<Input
disabled={true}
value={ProfileData ? moment(ProfileData.BirthDate).format('DD MMMM YYYY') : '' && formik.values.formA.ValueDesc5}
type="text"
name="formA.ValueDesc5"
onBlur={formik.handleBlur}
onChange={formik.handleChange}
/>
ProfileData is a state that contain User Profile
In my case, I use Next JS 13.0.4 + Chakra UI + Formik 2.2.9 and I face the same issues as yours.
I use SWR to fetch data from my server:
const fetcher = async (id: string): Promise<Course> => {
return (await findCourseById(id)) as Course;
...
const { data: dbCourse, error } = useSWR(
urlToServer,
() => fetcher(id),
{
refreshInterval: 1000,
},
);
};
The key to solve the problem is that we need to re-initialize the default values of the form. I use useFormik hook, and it looks like this:
const formik = useFormik({
initialValues: {
name: dbCourse?.name as string,
description: dbCourse?.description as string,
},
validationSchema: updateCourseSchema,
enableReinitialize: true, // <=== check this line
onSubmit: async (values) => {
console.log(values);
},
});
Let me know if it helps!

Formik initialValues and onSubmit issues

I am having a couple of issues with my Formik Form.
If I use defaultValue={location.fname} on the <TextField> I can type in the field,
but on submit the newly typed values do not appear in the alert.
If I use value={location.fname} on the <TextField> I can't type in the field.
What am I doing wrong? I would like to be able to type into the field to update the value AND get the new value onSubmit.
let formik = useFormik({
initialValues: {
fname: person.fname,
lname: person.lname,
address: person.address,
city: person.city,
},
enableReinitialize:true,
validateOnChange: false,
validateOnBlur: false,
validationSchema: validationSchema,
onSubmit: values => {
alert(JSON.stringify(values, null, 2));
},
});
...
<TextField
id="fname"
name="fname"
variant="outlined"
defaultValue={location.fname}
onChange={formik.setFieldValue}
error={formik.touched.fname && Boolean(formik.errors.fname)}
helperText={formik.touched.fname && formik.errors.fname} />
Change the defaultValue from location.fname to formik.values.fname as you are already passing the values to formik using initialValues. Also change the onChange from formik.setFieldValue to formik.handleChange and let formik handle the form state through the input name, finally add onBlur={formik.handleBlur} so that formik can know when your input field is touched.

Hey everyone! I want to keep my validation simple and still use a different conditions to every input in my function

I made some form validation in react, Can i use a few global variables from my state in one function for my form validation? Whats the way to keep it simple and still give a different conditions to every input? something wrong and I'd like to some help, I would like to not include the regexp pattern for the email or anything..
Thanks Everyone!
Registration.js
import React, { Component } from 'react'
export default class Registration extends Component {
state={
userName:'',
password:'',
email:'',
age:'',
backgroundColorInput:'white'
}
validLoginDetails=(item)=> {
locationOfS = email.indexOf('#');
this.setState({userName:item.target.value});
if(item.target.value.length>5 && item.target.value.length<9){
this.setState({backgroundColorInput:'green'})
}
this.setState({password:item.target.value});
if(item.target.value.length<7){
this.setState({backgroundColorInput:'red'})
}
this.setState({email:item.target.value});
if (item.target.value.locationOfS.indexOf(4) != -1) {
this.setState({backgroundColorInput:'green'} )
}
else{
this.setState({backgroundColorInput:'red'})
}
}
render() {
return (
<div>
<input placeholder='Enter your name' onChange={this.validLoginDetails} style={{backgroundColor: this.state.backgroundColorInput}} /><br/>
<input type='password' placeholder='Enter your a password' onChange={this.validLoginDetails} style={{backgroundColor: this.state.backgroundColorInput}} /><br/>
<input placeholder='Enter your Age' onChange={this.validLoginDetails} style={{backgroundColor: this.state.backgroundColorInput}} /><br/>
<input type="email" placeholder='Enter your Email' onChange={this.validLoginDetails} style={{backgroundColor: this.state.backgroundColorInput}} /><br/>
<button onClick={this.func}>submit</button>
</div>
)
}
}
App.js
import React from 'react';
import './App.css';
import Registration from './components/Registration.js';
import Main from './components/Main.js';
function App() {
return (
<div className="App">
<Registration/>
<Main/>
</div>
);
}
export default App;
Here are some of my recent problems and solutions that I hope will help you.
Recently I want to create a signup page with react. I need a library help me manage the form state and validate the form values. Then I choose the famous 'formik' + 'Yup'. But after some try, I found that it was not up to the job.
formik validate every fields when any fields value change or blur. For simple validation, it is not a big problem. But I need to check the usability of user name and email through network which may cost many time. And when checking, A spin is presented to the user. So the validation can't be triggered by change event. On the other hand, if the user name has been checked and not change, it should not be checked again. Becuase the user will not want to see a spin when he doesn't change the user name field. For almost all the form library, validation will be triggered before submit. but for those fields which has been validated and not changed yet, the validation should not be triggered. Formik is difficult to achieve the above requirements
At last I developed my own library. It support validate each field with separate triggers.
by and large there are three trigger: change, blur and submit.
For change, when the field value changed, the validation happen.
For blur, the situation will be a bit complicated. When the blur happen, if the field already has an error and the field has not changed from last validation, it should not be validated again. If the field has not an error, and the field value has not changed from last validation, it should not be validated again. If the field has not an error, but the field has been changed from last validation, it should be validated now.
For submit, similar to the 'blur' event, when submit happen, if the field already has an error and the field value has not been changed from last validation, no need to validate again, and terminate submission. If the field has not error and the field value has not been changed from the last validation, the field is considered to have been validated and does not need to be re-validated. If the field has not error and the field value has been changed from the last validation, a validation task should be started again.
There is a working demo
This is the code example
import React from 'react';
import { createValidator, useForm, OnSubmitFunction, REG_SPECIAL, string, sameWithWhenExists } from 'powerful-form-hook';
export const Demo = () => {
const initialValues = React.useMemo(() => ({
userName: '',
email: '',
password: '',
conformedPassword: '',
age: '',
}), []);
const validate = React.useMemo(() => createValidator<typeof initialValues>({
userName: (value) => {
if (!value) throw 'User name is required.'
if (value.length > 5 && value.length < 9) throw 'The length of user name should longer than 5 and shortter than 9.'
},
password: [
string()
.required('Password is required.')
.composedOf('Password must be composed of upper and lower case letters, Numbers and special keyboard symbols', /[a-z]+/i, /\d+/, REG_SPECIAL)
.matchSomeOf('The password must contain uppercase letters or special keyboard symbols', /[A-Z]+/, REG_SPECIAL)
.min(6, 'Password length must be greater than or equal to 6')
.max(24, 'Password length must be less than or equal to 24'),
// the validate task will trigger by blur of itself and conformedPassword field.
{
triggers: {
trigger: 'blur',
fields: ['conformedPassword'],
},
validate: sameWithWhenExists('conformedPassword', 'Enter the same password twice'),
},
],
conformedPassword: [
string('Password should be a string.'),
// the validate task will trigger by blur of itself and password field.
{
triggers: {
trigger: 'blur',
fields: ['password'],
},
validate: sameWithWhenExists('password', 'Enter the same password twice'),
},
],
age: string().matchSomeOf(/\d+/)
// same as userName.
email: [
string().required('Email is required.').trimmed('Email can not start or end with space.'),
],
}), []);
// Only when validate successful, the form submit. And if some validate task is running in the background or the form is submitting, the form will not be sumbited.
const onSubmit: OnSubmitFunction<typeof initialValues> = React.useCallback(async (values: typeof initialValues) => {
alert('submit successful');
}, []);
// handleChanges accept both event and value. For default, handleChanges will extract the value from event.target.value. For component like CheckBox which accept checked, use handleChanges[field].checked.
const { errors, values, handleChanges, handleBlurs, handleSubmit, submitting, validating } = useForm({
initialValues,
validate,
onSubmit,
});
return (
<form onSubmit={handleSubmit} noValidate>
<div>
<input placeholder='Enter your name' onChange={handleChanges.userName} onBlur={handleBlurs.userName} />
{ errors.userName.error ? errors.userName.message : null }
</div>
<div>
<input type='password' placeholder='Enter your a password' onChange={handleChanges.password} onBlur={handleBlurs.password} />
{ errors.password.error ? errors.password.message : null }
</div>
<div>
<input type='password' placeholder='Conform your a password' onChange={handleChanges.conformedPassword} onBlur={handleBlurs.conformedPassword} />
{ errors.conformedPassword.error ? errors.conformedPassword.message : null }
</div>
<div>
<input placeholder='Enter your Age' onChange={handleChanges.age} onBlur={handleBlurs.age} />
{ errors.age.error ? errors.age.message : null }
</div>
<div>
<input type="email" placeholder='Enter your Email' onChange={handleChanges.email} onBlur={handleBlurs.email} />
{ errors.email.error ? errors.email.message : null }
</div>
<button type="submit">submit</button>
{ submitting ? 'Submitting...' : validating ? 'Validating...' : null }
</form>
)
}

Formik Field validate using Yup

I was looking at the docs, and you can validate a Field by passing validate to Field which is a function.
e.g.
const validate = value => {
let errorMessage;
if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
errorMessage = 'Invalid email address';
}
return errorMessage;
};
...
<Field validate={validate} name="email" type="email" />
How can I substitute the function validate to use Yup ?
If you are going to ask why I'm doing this...
Instead of having a huge validationSchema object, I want to pass a Yup validation object directly to Field because my form is dynamic generated.
It seems like Formik doesn't provide a way to do this natively for some reason. You could do it like this:
<Field
name="email"
type="email"
validate={value =>
Yup.string()
.matches(/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i, "Invalid")
.validate(value)
.then(() => undefined)
.catch(({ errors }) => errors[0])
}
/>
Working example on CodeSandbox.

Resources