I have a project with nextjs and typescript in this project I've used react-hook-form for handling my forms. In one of my forms I have a checkbox and if that check box, be checked the Date input should be disabled and unrequired and otherwise the date input should be required.
I saw this solution
React-hook-form conditional validation of text field, based on if another checkbox is checked?
but I use Controller approach and I can't pass function into required this is my inputs
<div className='flex items-center gap-8'>
<InputWrapper
hasError={!!errors.expirationDate?.message}
errorMessage={errors.expirationDate?.message}
required
label='تاریخ انقضا'
disabled={isFormDisabled || watch('neverExpired')}>
<Controller
name="expirationDate"
control={control}
rules={{
required: {}
}}
render={({field}) => (
<CustomizedDatePicker
value={field.value}
minDate={new Date()}
name='expirationDate'
onChange={(date: DateObject) => {
field.onChange(date ? date.unix * 1000 : '');
}}
disabled={isFormDisabled || watch('neverExpired')}
/>
)}
/>
</InputWrapper>
<InputWrapper
labelBesideInput
hasError={!!errors.neverExpired}
label="هرگز منقضی نشود"
id={'neverExpireCheck'}
className={'pt-6'}
disabled={isFormDisabled}
>
<Controller
name="neverExpired"
control={control}
render={({field}) => (
<Checkbox
inputId={field.name}
onChange={(e) => field.onChange(e.checked)}
checked={field.value}
disabled={isFormDisabled}
/>
)}
/>
</InputWrapper>
tnx in advance for any effort
The best way is to use validation schemas such as Zod or yup, and you can manipulate the schema by the value of watch('neverExpired').
Related
I am using Formik in my React project to process forms and using MUI UI components.
I am able to pick the day, month, but the year part does not change. If I manually type in year in the textfield, the year part is not reflected in the changed state.
Here's my code:
<LocalizationProvider dateAdapter={DateAdapter}>
<DatePicker
name="birthday"
id="birthday"
variant="standard"
label="Birthday"
value={formik.values.birthday}
renderInput={(params) => (
<TextField {...params} variant="standard" fullWidth/>
)}
onChange={(value) => formik.setFieldValue('birthday', value, true)}
error={formik.touched.birthday && Boolean(formik.errors.birthday)}
helperText={formik.touched.birthday && formik.errors.birthday}
/>
</LocalizationProvider>
The initial state:
const initialFormState = {
birthday: Date.now(),
};
All the other components are working correctly and changes in state show immediately.
The onChange property is not set in the DatePicker component. You have to move the onChange property from TextField to DatePicker.
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
onChange={(value) => setFieldValue("birthday", value, true)}
value={values.birthday}
renderInput={(params) => (
<TextField
error={Boolean(touched.birthday && errors.birthday)}
helperText={touched.birthday && errors.birthday}
label="Birthday"
margin="normal"
name="birthday"
variant="standard"
fullWidth
{...params}
/>
)}
/>
</LocalizationProvider>
Also, the name, id, variant and label are TextField's properties.
Here is the working CodeSandbox link.
I have a short code below, the date picker works fine, it changes when I select other dates.
But i can't figure it out why it always fail in required validation even I already select a date.
<Controller
control={control}
name="disclosureDate"
rules={{ required: 'This field is required' }}
errors={errors.disclosureDate}
value={disclosureDate}
render={({ field }) => (
<InputDate
className="mb-px-8"
onChange={(value) => setDisclosureDate(value)}
value={disclosureDate}
/>
)}
/>
I've been following of these links, but I can't make it work in my end.
react-hook-link-Controllers
stackoverflow-link
(by the way, there are other validations aside from required, for demonstration purposes only)
I don't think you are using the Controller correctly. You should use the value and onChange it provides in the render prop, try -
<Controller
control={control}
name="disclosureDate"
rules={{ required: 'This field is required' }}
errors={errors.disclosureDate}
value={disclosureDate}
render={({ value, onChange }) => (
<InputDate
className="mb-px-8"
onChange={onChange}
value={value}
/>
)}
/>
I'm using Material UI version 4 (the latest), and the Informed form library. I have a custom component (custom to integrate with Informed) that wraps the Material UI TextField which I'm rendering using the Autocomplete component.
App component
<Form getApi={(api) => setFormApi(api)}>
{formApi && (
<>
<label>
First name:
<Autocomplete
freeSolo
options={autoOptions}
renderInput={(params) => (
<CustomTextField field="name" {...params} />
)}
/>
</label>
<button type="submit">Submit</button>
<button type="button" onClick={() => formApi.reset()}>
Reset
</button>
<FormState />
</>
)}
</Form>
The issue
When the reset button is clicked you can see the Informed "form state" is cleared, but the input still has a value. Any ideas on how to solve this?
Example - Codesandbox
The inputProps are getting overriden by the ones provided by Autocomplete component, change the order you pass ...rest props and included the ...rest.inputProps in your custom inputProps with the correct value
<TextField
{...rest} // should go first to allow overriding
// only add value props for select fields
// value={value}
onChange={(event) => {
setValue(event.target.value);
if (onChange) {
onChange(event);
}
}}
onBlur={(event) => {
setTouched(true);
if (onBlur) {
onBlur(event);
}
}}
error={!!error}
helperText={error ? error : helperText ? helperText : false}
variant="outlined"
margin="none"
fullWidth
inputProps={{
...rest.inputProps, // must include otherwise it breaks
value:
!select && !maskedValue && maskedValue !== 0 ? "" : maskedValue,
maxLength: maxLength || undefined
}}
// eslint-disable-next-line
InputProps={{
style: sensitive && {
color: "rgba(0,0,0,0)",
caretColor: "#000"
},
startAdornment
}}
InputLabelProps={{
shrink: true
}}
autoComplete="off"
disabled={disabled}
/>
I'm trying to make a form with two fields using react hook form where the required value of the text field depends on the value of the select drop down.
Here is my code:
const { handleSubmit, control, errors } = useForm();
const [isPickupPoint, togglePickupPoint] = useState(false);
const handleDestinationTypeChange: EventFunction = ([selected]) => {
togglePickupPoint(selected.value === "PICKUP_POINT");
return selected;
};
<Grid item xs={6}>
<InputLabel>Destination type</InputLabel>
<Controller
as={Select}
name="destinationType"
control={control}
options={[
{ label: "Pickup point", value: "PICKUP_POINT" },
{ label: "Shop", value: "SHOP" },
]}
rules={{ required: true }}
onChange={handleDestinationTypeChange}
/>
{errors.destinationType && (
<ErrorLabel>This field is required</ErrorLabel>
)}
</Grid>
<Grid item xs={6}>
<Controller
as={
<TextField
label="Pickup Point ID"
fullWidth={true}
disabled={!isPickupPoint}
/>
}
control={control}
name="pickupPointId"
rules={{ required: isPickupPoint }}
/>
{errors.pickupPointId && (
<ErrorLabel>This field is required</ErrorLabel>
)}
</Grid>
<Grid item xs={12}>
<Button
onClick={onSubmit}
variant={"contained"}
color={"primary"}
type="submit"
>
Save
</Button>
</Grid>
The isPickupPoint flag changes properly because the disabled prop of the textfield works fine. Only when the PICKUP_POINT option is selected the text field is active. But the required prop is not working, it is always false. When I try submitting the form when its empty the destinationType error label appears, but when I try to submit the form with the PICKUP_POINT option and empty pickupPointId field it passes with no errors.
How can I make this dynamic required prop work?
Based on the code here, it looks like isPickUpPoint is working as expected since it works for disable. Since you are using the same property for required, it should flow through. I would suspect that the bug may lie in your Controller component. I would take a look there and make sure that the property is what you expected to be.
Also for disabled the condition is !isPickUpPoint, so it will trigger when it's false.
For required the condition is isPickUpPoint so it will trigger when it's true.
That's also a bit of a disconnect since it looks like it's the same input.
Could you please tell why my required check is not working in autocomplete .I am using material UI with react hook form.
Step to reproduce
Click Submit button it show field is required.
then select any element from list.
remove the selected element Then click again submit button.It should
show “required” field check.but it is not showing anything why ??
Here is my code
https://codesandbox.io/s/mui-autocomplete-with-react-hook-form-0wvpq
<Controller
as={
<Autocomplete
id="country-select-demo"
multiple
style={{ width: 300 }}
options={countries}
classes={{
option: classes.option
}}
autoHighlight
getOptionLabel={option => option.label}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.label} ({option.code}) +{option.phone}
</React.Fragment>
)}
renderInput={params => (
<TextField
{...params}
label="Choose a country"
variant="outlined"
fullWidth
name="country"
inputRef={register({ required: true })}
// required
error={errors["country"] ? true : false}
inputProps={{
...params.inputProps,
autoComplete: "disabled" // disable autocomplete and autofill
}}
/>
)}
/>
}
onChange={([event, data]) => {
return data;
}}
name="country"
control={control}
/>
When the form loads initially, the value of your form is an empty object -
{}
When you select a country (say, 'Andorra') the value of your form becomes:
{"country":[{"code":"AD","label":"Andorra","phone":"376"}]}
And then when you deselect the country, the value of your form becomes:
{"country":[]}
An empty array technically meets the "required" criteria (it's not null, after all) so your required handler doesn't fire.
You can verify this is happening by showing the value of the form in your App class -
const { control, handleSubmit, errors, register, getValues } = useForm({});
return (
<form noValidate onSubmit={handleSubmit(data => console.log(data))}>
<Countries control={control} errors={errors} register={register} />
<Button variant="contained" color="primary" type="submit">
Submit
</Button>
<code>{JSON.stringify(getValues())}</code>
</form>
);
The simple fix is to NOT return an empty array as a value from your control - update your onChange handler as follows -
onChange={([event, data]) => {
return data && data.length ? data : undefined;
}}