I have a Field in a Formik which needs a dynamic validation Schema: When a user selects a payment token, the minimum payment value must be dynamically changed for another input field.
I used a state value and the "onChange" listener from the Field, and it works, except the displayed value
{token.symbol}
is not rendered any more.
As if overriding the "onChange()" field method had deactivated the Formik selection of this field.
Any idea what’s happening ?
My code:
import { ErrorMessage, Field, Form, Formik } from 'formik';
import { SetStateAction, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import * as Yup from 'yup';
import SubmitButton from './SubmitButton';
import useAllowedTokens from '../../hooks/useAllowedTokens';
interface IFormValues {
rateToken: string;
rateAmount: number;
}
const initialValues: IFormValues = {
rateToken: '',
rateAmount: 0,
};
function ServiceForm() {
const navigate = useNavigate();
const allowedTokenList = useAllowedTokens();
const [selectedToken, setSelectedToken] = useState('');
const validationSchema = Yup.object({
rateToken: Yup.string().required('Please select a payment token'),
rateAmount: Yup.number()
.required('Please provide an amount for your service')
.when('rateToken', {
is: (rateToken: string) => rateToken !== '',
then: schema =>
schema.moreThan(
allowedTokenList.find(token => token.address === selectedToken)
?.minimumTransactionAmount || 0,
`Amount must be greater than ${
allowedTokenList.find(token => token.address === selectedToken)
?.minimumTransactionAmount || 0
}`,
),
}),
});
// const onSubmit = async (values: IFormValues) => {
// <...>
// }
return (
<Formik initialValues={initialValues} onSubmit={onSubmit} validationSchema={validationSchema}>
{({ isSubmitting }) => (
<Form>
<div className='grid grid-cols-1 gap-6 border border-gray-200 rounded-md p-8'>
<div className='flex'>
<label className='block flex-1 mr-4'>
<span className='text-gray-700'>Amount</span>
<Field
type='number'
id='rateAmount'
name='rateAmount'
className='mt-1 mb-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50'
placeholder=''
/>
<span className='text-red-500 mt-2'>
<ErrorMessage name='rateAmount' />
</span>
</label>
<label className='block'>
<span className='text-gray-700'>Token</span>
<Field
component='select'
id='rateToken'
name='rateToken'
className='mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50'
placeholder=''
onChange={(event: { target: { value: SetStateAction<string> } }) => {
setSelectedToken(event.target.value);
}}>
<option value=''>Select a token</option>
{allowedTokenList.map((token, index) => (
<option key={index} value={token.address}>
{token.symbol}
</option>
))}
</Field>
<span className='text-red-500'>
<ErrorMessage name='rateToken' />
</span>
</label>
</div>
<SubmitButton isSubmitting={isSubmitting} label='Post' />
</div>
</Form>
)}
</Formik>
);
}
export default ServiceForm;
Tried: Overriding onChange() field method
Expected: The selected <option>’s value (token) should be updated in the form’s input data, but is not.
2
Answers
I had found another way, by extracting a child formik component:
Same idea.
To set a field value based on another field changes, you have to use setFieldValue from Formik, Here is the right way to do it
You have this issue because you didn’t set the value using the formmik setFieldValue
You have to set the value of the current field then set the value calculated for the other field