I am using material UI + react hook form .
https://react-hook-form.com/get-started
https://mui.com/material-ui/
I am trying to make datepicker. but I am facing one issue . My date picker open on top left why ?
here is my code
https://codesandbox.io/s/adoring-currying-iuptqe?file=/src/App.tsx
<Controller
control={control}
name="date"
rules={{
validate: {
min: (date) => isFuture(date) || "Please, enter a future date"
}
}}
render={({ field: { ref, onBlur, name, ...field }, fieldState }) => (
<DatePicker
{...field}
inputRef={ref}
label="Date"
renderInput={(inputProps) => (
<TextField
{...inputProps}
onBlur={onBlur}
name={name}
error={!!fieldState.error}
helperText={fieldState.error?.message}
/>
)}
/>
)}
/>
After some experiments, it seems that it could be because the posted sandbox is having the latest version of MUI, but pairing with an outdated version of #mui/x-date-pickers.
Updated #mui/x-date-pickers to the latest 5.0.13 to match the main MUI version and it looks to be fixed.
Forked demo with the update: codesandbox
Related
I'm creating a form which has a date field. I'm using MUI and react-hook-form for validation. I've tried to render the field in two different ways, but when submitting my form I'm not getting the expected value:
Render 1
Using a Controller component:
const [originalReleaseDate, setOriginalReleaseDate] = useState(null);
<Controller
name={"original_release_date"}
defaultValue={originalReleaseDate}
control={control}
render={({field}) =>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Original Release Date"
value={originalReleaseDate}
onChange={(newValue) => {
setOriginalReleaseDate(newValue);
}}
renderInput={(params) =>
<TextField
{...params}
/>}
/>
</LocalizationProvider>
}
/>
when I render the field this way, I'm getting null for original_release_date after submitting the form.
Render 2
Registering the field directly using {...register("reissue_release_date")} instead of react-hook-form Controlled component.
const [reissueReleaseDate, setReissueReleaseDate] = useState(null);
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Reissue Release Date"
value={reissueReleaseDate}
onChange={(newValue) => {
setReissueReleaseDate(newValue);
}}
renderInput={(params) =>
<TextField
{...params}
{...register("reissue_release_date")}
/>}
/>
</LocalizationProvider>
this way is working half way. If I manually type the date then I'm getting its value on submit, BUT if I use the date picker and then submitting the form I get "".
Any idea what's going on?
Just modified the above answer with a bracket.
const [reqDate, setreqDate] = useState(new Date());
<Controller
name="reqDate"
defaultValue={reqDate}
control={control}
render={
({ field: { onChange, ...restField } }) =>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Request Date"
onChange={(event) => { onChange(event); setreqDate(event); }}
renderInput={(params) =>
<TextField
{...params}
/>}
{...restField}
/>
</LocalizationProvider>
}
/>
InputDate.propTypes = {
name: PropTypes.string,
label: PropTypes.string,
};
export default function InputDate({ name, label }) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<LocalizationProvider dateAdapter={AdapterMoment} >
<DesktopDatePicker
label={label}
control={control}
inputFormat="DD-MM-YYYY"
value={value}
onChange={(event) => { onChange(event); }}
renderInput={(params) => <TextField {...params} error={!!error} helperText={error?.message} />}
/>
</LocalizationProvider>
)} />
)
}
In most cases, if you are using react-hook-form, you don't need to track form fields with useState hook.
Using a Controller component is the right way to go. But there is a problem with onChange handler in your 1st method.
When you submit form, you are getting default date null because field is destructed, but it's not passed to DatePicker. So, onChange prop of field is not triggered when date is changed and react-hook-form doesn't have new date.
Here's how your render method should be
render={({field}) =>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Original Release Date"
renderInput={(params) =>
<TextField
{...params}
/>}
{...field}
/>
</LocalizationProvider>
}
If for some reason, you need to update component state then you have to send data to react-hook-form and then update local state
render={({field: {onChange,...restField}) =>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Original Release Date"
onChange={(event) => {
onChange(event);
setOriginalReleaseDate(event.target.value);
}}
renderInput={(params) =>
<TextField
{...params}
/>}
{...restField}
/>
</LocalizationProvider>
}
I couldn't replicate your setup, but my guess is that in the first render the
reference to the 'setOriginalReleaseDate' is lost when being passed through the Controller's render arrow function.
...
onChange={(newValue) => {
setOriginalReleaseDate(newValue);
}}
...
so, try putting the logic in a defined function like:
const handleOriginalReleaseDateChange = (newValue) => {
setOriginalReleaseDate(newValue);
};
and change the onChange to call the function.
...
onChange={handleOriginalReleaseDateChange}
...
what happens if I select a date by clicking on the calendar it works fine but if I set inputFormat="yyyy/MM/dd" then I will type the date it will not react like date format its go like 11111111111111111111 it is considered as a string like this so its break the format of date but if I select from the calendar it works fine but only if I will edit direct type it goes wrong.
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Basic example"
value={value}
inputFormat="yyyy/MM/dd"
onChange={(newValue) => {
setValue(newValue);
}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
add mask props to your code.
<DatePicker
...
mask="____/__/__"
/>
I'm using
"react-hook-form": "7.9.0",
And
"#material-ui/core": "4.11.4",.
I'm trying to reset some checkbox manually by clicking on regular button and using the reset (react-hook-form reset) method of react-hook-form.
For some reason I can see in react dev tools that the "checked" prop is changing to false but the SwitchBase (v icon) is still on.
You can see example: Over here
Thank you for your time.
The MUI <Checkbox /> component has a slighty different interface for setting a value. The RHF value has to be set with checked prop.
One important note: you have to set the value with checked={!!value} to avoid a warning about changing a component from uncontrolled to controlled in case that your defaultValue for someCheckbox is initially undefined.
export const Checkbox = ({
name,
label,
control,
labelPlacement,
disabled,
className
}) => {
return (
<FormControlLabel
label={label}
disabled={disabled}
labelPlacement={labelPlacement}
className={className}
control={
<Controller
name={name}
control={control}
rules={{ required: true }}
render={({ field: { value, ...field } }) => (
<MuiCheckbox
{...field}
checked={!!value}
/>
)}
/>
}
/>
);
};
The controller hook uses value (which is true/false) and not checked (which is what Checkbox of Material-UI expects) in order to set the check-status of the checkbox.
In order to fix your issue you can take the value from the field (inside the render property of the Controller) and set it to the checked property of the Checkbox:
control={
<Controller
name={name}
control={control}
rules={{ required: true }}
render={({ field }) => <MuiCheckbox {...field} checked={field['value'] ?? false} /> }
/>
}
I am using React Bootstrap form control and input group.
I have created one Component and used in my other modules.
The code is as follows :
import InputGroup from "react-bootstrap/InputGroup";
import Form from "react-bootstrap/Form";
export default class TextInput extends Component {
render() {
var { value, maxLength, defaultInput, prefix, inputProps, placeholder, label, labelClass, formGroupClass, inputType, error, warning, onChange, disabled, mandatory } = this.props;
return (
<div className={styles.container}>
<Form.Group className={formGroupClass}>
{label?
(<Form.Label className={labelClass}>{label}
{mandatory ? <span className="text-danger"> *</span>
: ""}
</Form.Label>)
: ""
}
<InputGroup>
{prefix && prefix !== "" ?
<InputGroup.Prepend>
<InputGroup.Text>{prefix}
</InputGroup.Text>
</InputGroup.Prepend> : ""}
<Form.Control isInvalid={!!error} className={warning ? "border border-warning " : undefined}
disabled={disabled}
type={inputType}
placeholder={placeholder}
value={value}
defaultValue={defaultInput}
maxLength={maxLength}
onChange={(e) => onChange(e.target.value)}
onKeyPress={(e) => this.onKeyUp(e)}
{...inputProps}
/>
</InputGroup>
<Form.Text className="feedback-warn text-warning">
{warning}
</Form.Text>
<Form.Control.Feedback type="invalid">
{error}
</Form.Control.Feedback>
</Form.Group>
</div>
);
}
}
}
And used this component in my other modules like this :
<TextInput
placeholder={formData.mobileNum.label}
inputType={"number"}
error={formData.mobileNum.errMsg}
value={formData.mobileNum.value}
prefix={"+91"}
maxLength="10"
onChange={(text) =>
changeValue(formData.mobileNum.propName, text)
}
/>
The problem I am facing is in android device when I click on input field the number keypad is working fine.
In case of ios device inputType={"number"} is not working as I am not able to get the number keypad.
I tried some fixes but they affect the android also. I want that number keypad comes in both of the devices when I try to enter input field.
<TextInput
placeholder={formData.mobileNum.label}
error={formData.mobileNum.errMsg}
value={formData.mobileNum.value}
prefix={"+91"}
maxLength="10"
keyboardType="numeric" //add this
onChange={(text) =>
changeValue(formData.mobileNum.propName, text)
}
/>
add keyboardType in your text input and remove inputType
I resolved the issue.
Done some silly mistake of not noticing the {...inputProps} it contains all the remaining props for input.
In my TextInput component simply added the methods : pattern and inputMode
var { pattern, inputMode } = this.props
<Form.Control isInvalid={!!error} className={warning ? "border border-warning " : undefined}
disabled={disabled}
type={inputType}
placeholder={placeholder}
value={value}
pattern={pattern} // add this
inputMode={inputMode} // add this
defaultValue={defaultInput}
maxLength={maxLength}
onChange={(e) => onChange(e.target.value)}
onKeyPress={(e) => this.onKeyUp(e)}
{...inputProps}
/>
Then in my other files simply used these props like this :
<TextInput
..other props
pattern={"[0-9]*"}
inputMode={"numeric"}
onChange={(text) =>
changeValue(formData.mobileNum.propName, text)
}
/>
pattern={"[0-9]*"} and inputMode={"numeric"} will force this ios devices to use number dialpad. Although pattern prop alone is enough inputMode is just a fallback if pattern doesn't work.
Note -> This will not affect the android behaviour with number keypad.
Alternative :
Instead of changing contents in the Main component we can utilise {...inputProps}
<TextInput
...other props
inputProps={{ pattern: "[0-9]*", inputMode: "numeric" }} //add this
onChange={(text) =>
changeValue(formData.mobileNum.propName, text)
}
/>
My basic forms work fine, however, as there are some caveats with react-native, I could use some help in knowing if the issue is how I am using formik, or using it with react-native.
In this particular form, when I attempt to fill in a picker in react-native using formik, the form resets the picker to the original value immediately after I select an option. I have stripped the code down, as I feel someone should have the answer without a lot of code, but I am not seeing what I am missing. Thanks.
<Formik
onSubmit={
props.onSubmit(props.values)
}
mapPropsToValues = {(props) => ({
id: props.id,
status: props.status
})}
validate={values => {
// same as above
let errors = {};
return errors;
}}
onValueChange={ (itemIndex) => {
this.props.values.status = itemIndex
}}
render= { props => (
const { id, status } = this.props
<View>
<Text style={styles.textResultsHeaderStyle}>Job: {id}</Text>
<Picker
selectedValue={status}
onValueChange={itemIndex => this.onValueChange}>
<Picker.Item label="New" value="0" />
<Picker.Item label="Requested" value="1" />
<Picker.Item label="Responded" value="2" />
<Picker.Item label="Closed" value="3" />
</Picker>
<RoundedButton disabled={props.isSubmitting} onPress={props.handleSubmit} text="SUBMIT" />
</View>
)}
/>
I just answered a similar question on github. I suppose you're using the built in picker component in RN. If not then you need to check the documentation for your picker component to see how to get the value on change.
https://github.com/jaredpalmer/formik/issues/1378#issuecomment-480189488