0
votes

I am using Yup validation along with formik. I have both initial values and validationSchema prepared and everything works fine during creating a form. But on updating the form, even when the values are pre-populated in their respective fields, still some of the fields say 'abc field is required'. (name and type fields are required according to the below issue)

const initialValues = {
type: '',
name: '',
detail: {
    shareDetail: {
        tenantId: '',
        username: ''
    },
    blobDetail: {
        access: '',
        connection: ''
    }
}
}

const validationSchema = Yup.object().shape({
name: Yup.string().required(),
type: Yup.string().required(),
detail: Yup.object().required().when('type', {
    is: (selectedType) => selectedType === 'SHARE',
    then: Yup.object({
        shareDetail: Yup.object({
            tenantId: Yup.string().required(),
            username: Yup.string().required()
        })
    }),
    is: (selectedType) => selectedType === 'BLOB',
    then: Yup.object({
        blobDetail: Yup.object({
            access: Yup.string().required(),
            connection: Yup.string().required()
        })
    })
})
}, ['type'])

and my jsx is:

 <Formik initialValues={formData || initialValues} validationSchema={validationSchema} onSubmit={onSubmit} enableReinitialize validateOnMount>
            {formik => {
                return (
                    <Form>
                        <h5>Source</h5>
                        <div className='row'>
                            <div className='col-6'>
                                <FormControl control='select' label='Type *' placeholder='Enter Source Type' name='type' options={sourceTypes} AutoComplete
                                    value={getSourceTypeValue(formik.values.type)} onChange={e => handleSourceTypeSelect(e, formik.setFieldValue)} onBlur={() => handleSourceTypeBlur(formik.setFieldTouched)}
                                    outlineDanger={formik.errors.type && formik.touched.type ? 'mnd-danger' : ''} isDisabled={type !== 'createSourceButton'} />
                            </div>
                            <div className='col-12 mt-2'>
                                <FormControl control='input' type='text' label='Name *' placeholder='Enter Source Name' name='name'
                                    version={formik.touched.name && formik.errors.name ? 'mnd-danger' : ''} disabled={type !== 'createSourceButton'} />
                            </div>
                            <div className='col-12'>
                                <FormControl control='textarea' type='text' placeholder='Enter Source Description' label='Source Description' rows='3' name='description' />
                            </div>
                        </div>
                        <hr />
                        {formik.values.type === 'SHARE' && <>
                            <h5>Source Details</h5>
                            <div className="row mt-3">
                                {type === 'createSourceButton' && <div className="col-6 mt-2">
                                    <FormControl control='input' type='text' label='Tenant Id *' placeholder='Enter Tenant Id' name='detail.shareDetail.tenantId'
                                        version={formik.touched.detail && formik.errors.detail && formik.touched.detail.shareDetail.tenantId && formik.errors.detail.shareDetail.tenantId ? 'mnd-danger' : ''} />
                                </div>}
                                <div className="col-6">
                                    <FormControl control='input' type='text' label='Username *' placeholder='Enter Username' name='detail.shareDetail.username'
                                        version={formik.touched.detail && formik.errors.detail && formik.touched.detail.shareDetail.username && formik.errors.detail.shareDetail.username ? 'mnd-danger' : ''} />
                                </div>
                            </div>
                            <hr />
                        </>}
                        {formik.values.type === 'BLOB' && <>
                            <h5>Source Details</h5>
                            <div className="row mt-3">
                                <div className="col-12">
                                    <FormControl control='textarea' type='text' label='Connection String *' placeholder='Enter Connection String' rows='3' name='detail.blobDetail.connection'
                                        variant={formik.touched.detail && formik.errors.detail && formik.touched.detail.blobDetail.connectionString && formik.errors.detail.blobDetail.connectionString ? 'mnd-danger' : ''} />
                                </div>
                                <div className="col-12">
                                    <FormControl control='select' label='Access *' placeholder='Select Access' name='detail.blobDetail.access' options={accessOptions}
                                        value={getAccessTierValue(formik.values.detail.blobDetail.accessTier)} onChange={e => handleAccessTierSelect(e, formik.setFieldValue)} onBlur={() => handleAccessTierBlur(formik.setFieldTouched)}
                                        outlineDanger={formik.touched.detail && formik.errors.detail && formik.touched.detail.blobDetail.access && formik.errors.detail.blobDetail.access? 'mnd-danger' : ''} />
                                </div>
                            </div>
                            <hr />
                        </>}
                        <div className="btn-toolbar float-right" role="toolbar">
                            <div className="btn-group mr-2" role="group">
                                <Button variant='outline' onClick={() => handleCancel()}>Cancel</Button>
                            </div>
                            <div className="btn-group mr-2" role="group">
                                {type === 'createSourceButton' && <Button type='reset' onClick={() => resetForm()}>Reset</Button>}
                            </div>
                            <div className="btn-group" role="group">
                                <Button type='submit' disabled={!formik.isValid} onSubmit={() => onSubmit()}>
                                    {type === 'createSourceButton' ? 'Create' : 'Update'}
                                </Button>
                            </div>
                        </div>
                    </Form>
                )
            }}
        </Formik>

Everything is working fine during the source creation scenario, but the update button is being disabled and if I check the formik errors, it says name and type are required fields even if their respective values are being populated correctly in the UI.

2
I suggest that you remove the required() from the beginning if the expression and add it to the end along with nullable() as for the nested objects validation discussed github.com/jquense/yup/issues/501niiima
@niiima, thanks for the suggestion. But that did not work.Panchakshari Puranmatt
I loaded formData with setTimeout of 10 milliseconds inside useEffect() and everything started to work fine. I was using useEffect() before as well with the conditional parameter to load formData but didn't use setTimeout. Not sure why it didn't work.Panchakshari Puranmatt
Have you tried to remove the ["type"] as a dependency. I don't see a point to that, there also might be a problem using yup.object() directly for the nested level, instead please change them to yup.object().shape({}).niiima

2 Answers

0
votes

During the definition of the initial values you've set all the props for both cases of the detail property, it causes problem as with each value of the type property, both properties will still exist on the main object. You might wand to try something like:

const initialValues = {type: '', name: '', detail:{}};

const validationSchema = Yup.object().shape({
name: Yup.string().required(),
type: Yup.string().required(),
detail: Yup.object().when('type', {
    is: (selectedType) => !!selectedType && selectedType === 'SHARE',
    then: Yup.object.shape({
        shareDetail:Yup.object().shape({
            tenantId: Yup.string().required(),
            username: Yup.string().required()
        })
    }),
    is: (selectedType) => !!selectedType &&  selectedType === 'BLOB',
    then: Yup.object.shape({
        blobDetail: Yup.object().shape({
            access: Yup.string().required(),
            connection: Yup.string().required()
        })
    }),
}).nullable().required() // you might also need this.
}); //, ['type'])
0
votes

Problem might be with enableReinitialize prop you are passing to formik. In Docs states:

enableReinitialize?: boolean

Default is false. Control whether Formik should reset the form if initialValues changes (using deep equality).

https://formik.org/docs/api/formik#enablereinitialize-boolean