Formik with react-day-picker - javascript

I would like to create a form with a date picker. So i choose React DayPicker v8 now i would like to implement this picker in my current formik form.
I created a date picker component:
import React from "react";
import { Form, Popover, OverlayTrigger } from "react-bootstrap";
import { DayPicker, useInput } from "react-day-picker";
import "react-day-picker/dist/style.css";
const DatePicker = (props) => {
const { inputProps, dayPickerProps } = useInput({
format: "dd.MM.yyyy",
required: true,
});
const handleChange = (value) => {
props.onChange(props.name, value);
};
const popover = (
<Popover id="popover-basic" className="mw-100">
<Popover.Body>
<DayPicker showWeekNumber weekStartsOn={1} {...dayPickerProps} />
</Popover.Body>
</Popover>
);
return (
<>
<OverlayTrigger
rootClose
trigger="click"
placement="bottom-start"
overlay={popover}
>
<Form.Control
{...inputProps}
onChange={handleChange}
value={props.value}
name={props.name}
id={props.name}
/>
</OverlayTrigger>
</>
);
};
export default DatePicker;
DatePicker.js
<Col>
<Form.Group className="mb-3">
<Form.Label htmlFor="startdate">Start Date</Form.Label>
<DatePicker
name="startdate"
value={formikProps.values.startdate}
onChange={formikProps.setFieldValue}
/>
</Form.Group>
</Col>
Home.js
My problem is in my browser DevTools the value from datepicker changed correctly <input name="startdate" id="startdate" class="form-control" value="23.06.2022"> but in my formikProps.values.startdate I only get the old initialValue.
Additionally if i add a log event to the handleChange function in the DatePicker.js Component the log will not show up. Why?
Is this the correct way? In a react-select field this code works completely fine.

My solution was
<Popover id="popover-basic" className="mw-100">
<Popover.Body>
<DayPicker
{...dayPickerProps}
showWeekNumber
weekStartsOn={1}
onDayClick={handleChange}
/>
</Popover.Body>
</Popover>
The keyword was OnDayClick not OnChange

Related

Custom input from react-form-hook not working

Following the example here I have a custom input component:
Input.tsx
import React from "react";
export default function Input({label, name, onChange, onBlur, ref}:any) {
return (
<>
<label htmlFor={name}>{label}</label>
<input
name={name}
placeholder="Jane"
onChange={onChange}
onBlur={onBlur}
ref={ref}
/>
</>
);
}
An example of the usage:
import {useAuth} from '../components/AuthContextProvider'
import { useForm, SubmitHandler } from "react-hook-form";
import Input from '../components/Input'
function Subscriptions({ Component, pageProps }: any) {
const { user } = useAuth()
const { register, handleSubmit, formState: { errors } } = useForm<Inputs>();
const onSubmit: SubmitHandler<Inputs> = async data => {
console.log(data, 'data sdfdsg')
}
return (
<>
<form onSubmit={handleSubmit(onSubmit)}>
<Input label="name" {...register('name')}/>
<button type='submit'>Add kid</button>
</form>
</>
)
}
export default Subscriptions
Here's my package version:
"react-hook-form": "^7.34.2"
Any ideas what I'm doing wrong here?
The custom input receives undefined but it works with normal <input /> tags.
I've used the example in a codesandbox and it threw errors about ref and it suggested using React.forwardRef, change your custom Input to this:
function Input({ label, name, onChange, onBlur }, ref) {
return (
<>
<label htmlFor={name}>{label}</label>
<input
name={name}
placeholder="Jane"
onChange={onChange}
onBlur={onBlur}
ref={ref}
/>
</>
);
}
const MyInput = React.forwardRef(Input);
export default MyInput;
and by the way, ref is not part of props I don't know why the example has it like that, it's necessary to use forwardRef so it is passed as second argument.
you can see the full example in this codesandbox

MUI DatePicker's shouldDisableDate prop leading to error

<Controller
name="toDate"
control={control}
defaultValue={null}
render={({ field }) => (
<DatePicker
format="DD/MM/yyyy"
value={field.value}
onChange={(e) => { setToDate(e); field.onChange(e); }}
minDate={fromDate}
shouldDisableDate={fromDate}
/>
)}
{...register("toDate",{ required: true })}
/>
I used the shouldDisableProp to disable the date which is selected inside another datepicker whose value is stored in a state variable 'fromDate'.
But when I press the datepicker it is leading to this error.
The minDate is working perfectly fine using the fromDate state variable.
The shouldDisableDate method receives a date param and expect a boolean.
import * as React from "react";
import TextField from "#mui/material/TextField";
import AdapterDateFns from "#mui/lab/AdapterDateFns";
import LocalizationProvider from "#mui/lab/LocalizationProvider";
import DatePicker from "#mui/lab/DatePicker";
export default function BasicDatePicker() {
const [value, setValue] = React.useState<Date | null>(null);
const [fromDate, setFromDate] = React.useState<Date | null>(null);
return (
<>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="From Date"
value={fromDate}
onChange={(newValue) => {
setFromDate(newValue);
}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<br />
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="To Date"
value={value}
onChange={(newValue) => {
setValue(newValue);
}}
minDate={fromDate}
shouldDisableDate={(dateParam) => {
//your_condition here
//return true to disabled and false to enable
const fromFormatted = fromDate.toISOString().split("T")[0];
const currentDate = dateParam.toISOString().split("T")[0];
return fromFormatted === currentDate ? true : false;
}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
</>
);
}
UpdatedCode
So do something like that and you will achieve your goal.
In your case, compare dateParam with your fromDate var, to return true or false and disable or not the date.
Edit: Now is adapted to your real case. Just check it out.

TypeError: props.render is not a function (React hook form)

I am passing methods as a prop in this form I am making with react-hook-form.
Its giving me (TypeError: props.render is not a function) when Controller is added in from react-hook-form. I cannot find any solutions online so any help is appreciated.
import { useForm, FormProvider } from 'react-hook-form';
import FormInput from './CustomTextField';
const AddressForm = () => {
const methods = useForm();
return (
<>
<FormProvider {...methods}>
<form onSubmit=' '>
<Grid container spacing={3}>
<FormInput required name='firstName' label='First name' />
</Grid>
</form>
</FormProvider>
</>
);
};
import { useFormContext, Controller } from 'react-hook-form';
const FormInput = ({ name, label, required }) => {
const { control } = useFormContext();
return (
<>
<Controller
as={TextField}
name={name}
control={control}
label={label}
fullWidth
required={required}
/>
<>
);
};
export default FormInput;
Was stuck in somewhat similar problem,
you can try following changes in FormInput function:
import React from 'react';
import { TextField, Grid } from '#material-ui/core';
import { useFormContext, Controller } from 'react-hook-form';
const FormInput = ({ name, label, required}) => {
const { control } = useFormContext();
const isError = false;
return (
<>
<Controller
control={control}
name={name}
render = {({ field})=> (
<TextField
fullWidth
label={label}
required
/>
)}
/>
</>
);
}
export default FormInput;
hope this helps, else you can go through the docs
I had this problem (TypeError: props.render is not a function) with react-hook-form + react-select when I tried to reuse the same Controller component from an old project, and I fixed this way:
Unchanged:
import { useForm, Controller } from "react-hook-form";
import Select from "react-select";
From:
<Controller
name="languages"
control={control}
rules={{ required: true }}
as={Select}
options={props.languageOptionsToSelect}
defaultValue={props.languageDefaultValueToSelect}
isMulti
/>;
To:
<Controller
name="languages"
control={control}
rules={{ required: true }}
render={({ field }) => (
<Select
{...field}
options={props.languageOptionsToSelect}
defaultValue={props.languageDefaultValueToSelect}
isMulti
/>
)}
/>;
It seems render prop in Controller component is required now.
This problem is arising either because you update your react-hook-form or new to react-hook-form
You just need to use render prop in Controller component
<Controller
render={({ field }) => (
<input
onChange={(e) => field.onChange(transform.output(e))}
value={transform.input(field.value)}
/>
)}
/>
or if you are using a third party Form library
import { Input, Select, MenuItem } from "#material-ui/core";
<Controller
render={({ field }) => (
<Select {...field}>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
</Select>
)}
control={control}
name="select"
defaultValue={10}
/>
Try this one!
<Controller
render={({ field }) => <TextField {...field} />}
name={name}
control={control}
label={label}
fullWidth
required={required}
/>
Add render as a prop in the Controller Component. Refer to the docs here
import React from 'react'
import { TextField, Grid } from '#material-ui/core'
import { useFormContext, Controller } from 'react-hook-form'
const FormInput = ({ name, label, required }) => {
const { control } = useFormContext()
return (
<Grid item xs={12} sm={6}>
<Controller
control={control}
name={name}
render = {({ field})=> (
<TextField
fullWidth
label={label}
required
/>
)}
/>
</Grid>
)
}
export default FormInput
It now requires a render props, try this on MUI:
<Controller
render={({ field }) => (
<TextField {...field} label={label} required={required}/>)}
control={control}
fullWidth
name={name}
/>

Delete tag in uncontrolled "ChipInput" using react hook form

I'm using react-hook-form to handle form values, Its working fine for all other input types like TextFeild, Select from material but facing issues with "material-ui-chip-input" as adding tag working fine but not able to delete tag on click of cross button or hitting backspace. I'm struggling on this from a long. Anyone please help in it.
import React from "react";
import FormControl from "#material-ui/core/FormControl";
import { Controller } from "react-hook-form";
import ChipInput from "material-ui-chip-input";
const ReactHookFormChips = ({
name,
label,
control,
defaultValue,
children,
rules,
error,
chipsError,
...props
}) => {
const labelId = `${name}-label`;
return (
<FormControl {...props}>
<Controller
as={
<ChipInput
label={label}
helperText={chipsError}
error={error}
/>
}
name={name}
control={control}
defaultValue={defaultValue}
rules={rules}
/>
</FormControl>
);
};
export default ReactHookFormChips;
calling this component like
<ReactHookFormChips
id="levelLabel"
name="tags"
label="Select Tags"
control={control}
defaultValue={[]}
margin="normal"
error={!!errors?.tags}
rules={{ required: true }}
chipsError={errors?.tags && "Tag is required."}
/>
I fixed it using render prop.
import React from "react";
import FormControl from "#material-ui/core/FormControl";
import InputLabel from "#material-ui/core/InputLabel";
import { Controller } from "react-hook-form";
import ChipInput from "material-ui-chip-input";
const ReactHookFormChips = ({
name,
label,
control,
defaultValue,
children,
rules,
error,
chipsError,
...props
}) => {
const labelId = `${name}-label`;
return (
<FormControl {...props}>
<Controller
render={({ onChange, onBlur, value }) =>
<ChipInput
onChange={onChange}
label={label}
helperText={chipsError}
error={error}
/>
}
name={name}
control={control}
defaultValue={defaultValue}
rules={rules}
/>
</FormControl>
);
};
export default ReactHookFormChips;

How to pass Material UI data to Formik?

I am trying to add a Material UI 'Select' to my Formik component, but can't pass the values to Formik's initialValues.
const [hours, setHours] = React.useState('');
const handleHourChange = ({ target }) => {
setHours(target.value);
};
<Formik
initialValues={{
price: '' //not Material UI. Works.
hours: hours //from Material UI
}}
<Form>
<label htmlFor={'price'}> Price </label>
<Field
name={'price'}
type="text" //this Field (not Material UI) works fine.
/>
//...
//the below, which is Material UI's
//doesn't send its values to Formik's initialValues
<FormControl className={classes.formControl}>
<InputLabel id="demo-simple-select-label">Hours</InputLabel>
<Select
name={'hours'} //I added this name prop but not sure it's making any difference
labelId="demo-simple-select-label"
id="demo-simple-select"
value={hours}
onChange={handleHourChange}>
<MenuItem value={60}>01</MenuItem>
<MenuItem value={120}>02</MenuItem>
</Select>
</FormControl>
</Form>
</Formik>
What could be done to fix this? I don't seem to know how to properly get Material UI's values to add them to Formik.
For material ui, you can use this great library
https://github.com/stackworx/formik-material-ui
Here is their sample codesandbox
https://codesandbox.io/s/915qlr56rp?file=/src/index.tsx
If you don't want to use their library, you can use Formik's component prop to use your custom component
Based on your comment request, I've written the code here at codesandbox
https://codesandbox.io/s/react-formik-material-ui-select-huzv7?file=/src/App.js
I'm not sure why you have state variables for formik values. Those are handled by formik. We don't need to handle them manually.
import React from "react";
import { Formik, Form, Field } from "formik";
import {
Select,
InputLabel,
MenuItem,
FormControl,
Button
} from "#material-ui/core";
import "./styles.css";
const CustomizedSelectForFormik = ({ children, form, field }) => {
const { name, value } = field;
const { setFieldValue } = form;
return (
<Select
name={name}
value={value}
onChange={e => {
setFieldValue(name, e.target.value);
}}
>
{children}
</Select>
);
};
export default function App() {
/*
You don't need to handle the formik values as state.
Formik handles it itself
const [hours, setHours] = React.useState("");
const handleHourChange = ({ target }) => {
setHours(target.value);
};
*/
return (
<Formik
initialValues={{
price: "abcv", //not Material UI. Works.
hours: 60 //from Material UI
}}
onSubmit={(values, actions) => {
alert("values:" + JSON.stringify(values));
}}
>
<Form>
<label htmlFor={"price"}> Price </label>
<Field
name={"price"}
type="text" //this Field (not Material UI) works fine.
/>
<FormControl>
<InputLabel id="demo-simple-select-label">Hours</InputLabel>
<Field name="hours" component={CustomizedSelectForFormik}>
<MenuItem value={60}>01</MenuItem>
<MenuItem value={120}>02</MenuItem>
</Field>
</FormControl>
<Button type="submit">Submit</Button>
</Form>
</Formik>
);
}

Categories