How to use custom Input with Formik in React? - reactjs

I'm trying to use DatePicker within Formik. But when I click DatePicker's date its form value is not changed. Instead, I got this error:
Uncaught TypeError: e.persist is not a function
at Formik._this.handleChange (formik.es6.js:5960)
I shortify code, the code is below
const SomeComponent = () => (
<Formik
render={({
values,
handleSubmit,
handleChange,
setFieldValue
}) => {
return (
<div>
<form onSubmit={handleSubmit}>
<DatePicker
name={'joinedAt'}
value={values['joinedAt']}
onChange={handleChange}
/>
</form>
</div>
)
}}
/>
)
I googled few documents, https://github.com/jaredpalmer/formik/issues/187 and https://github.com/jaredpalmer/formik/issues/86
So I tried like below, but it's not working.
...setFieldValue
<DatePicker
name={'joinedAt'}
value={values['joinedAt']}
onChange={setFieldValue}
/>

I resolve this like
<DatePicker
name={'joinedAt'}
value={values['joinedAt']}
onChange={e => setFieldValue('joinedAt', e)}
/>
Update on 2020-03-08:
You can use e.target.value on setFieldValue's second prop, depends on your custom input design. Thanks to Brandon.

If you have deeper nesting, you should use Formik Field.
Example:
<Formik
validationSchema={schema}
initialValues={initialValues}
onSubmit={(values, actions) => {}}
>
<Field name="colors" component={ColorsEditor} colors={colorArray}/>
</Formik>
const ColorsEditor = ({ field, colors, form, ...props }) => {
return (
<div>
<Button onClick={() => form.setFieldValue('colors', "myValue")}>
</Button>
</div>
)
}
So the Field component already include the form, where live the setFieldValue that you can use where you need. It also include the errors and needed fields for additional customization.

For html primitive input fields handleChange works like a charm, but for custom components, you've to use setFieldValue to change the value imperatively.
onChange={e => setFieldValue('joinedAt', e)}

The accepted answer didn't work for me and didn't show the selected date. It has been almost a year so there might be some changes with the implementation. Though this can be fix by using toDateString() like this
<DatePicker
name={'joinedAt'}
value={values['joinedAt']}
onChange={e => setFieldValue('joinedAt', e.toDateString())}
/>

The Formik solution for this is the useField() method. You may need to create a wrapper for components like DatePicker that you may not have source access to.
The documentation provides an example:
const MyTextField = ({ label, ...props }) => {
const [field, meta, helpers] = useField(props);
return (
<>
<label>
{label}
<input {...field} {...props} />
</label>
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
);
};
In short, the method takes in the custom props which describe the field. Then you can expand the assigned field value and props in your custom field.

https://github.com/jaredpalmer/formik/issues/692
you need to set value manually if you are using setFieldValue method

Related

How to use RadixUI with React Hook Form's Controller

I'm unable to use Radix UI's select component with React Hook Form's Controller and don't understand why the following doesn't work in this CodeSandbox. Any ideas?
For example, the Radix UI documentation shows passing value and onChange props to <SelectPrimitive.Root> as well as the ref to <SelectPrimitive.Trigger>
export const Select = React.forwardRef(
({ children, ...props }, forwardedRef) => {
return (
<SelectPrimitive.Root {...props}>
<SelectPrimitive.Trigger ref={forwardedRef}>
<SelectPrimitive.Value />
...
</SelectPrimitive.Root>
How I implemented it:
const SelectItem = forwardRef(({ value, onValueChange }, forwardedRef) => {
return (
<Select.Root value={value} onValueChange={onValueChange}>
<Select.Trigger
ref={forwardedRef}
className="text-md border-solid border-2 border-slate-500 px-2"
>
...
</Select.Root>
How I pass value and onChange in WrapperSelect using RHF's Controller
<Controller
control={control}
name={name}
render={({ field: { onChange, value, ref, ...props } }) => (
<SelectItem
valueOnChange={onChange}
value={value}
forwardedRef={ref}
/>
)}
/>
I had similar problem, also with Radio button and I found out that you need to use custom <Select.Value> component with asChild prop
<Select.Value asChild>
<span>{props.value ?? "Select something..."}</span>
</Select.Value>
Here is a working sandbox: https://codesandbox.io/s/admiring-sun-xwupmz?file=/src/atoms/SelectItem.tsx

react hook form's getValues() returning undefined

The Question:
How do I access the form's values using react-hook-form's Controller without using setValue() for each input?
The Problem:
I'm creating my own set of reusable components and am trying to use React Hook Form's Controller to manage state, etc. I'm having trouble accessing an input's value and keep getting undefined. However, it will work if I first use setValue().
CodeSandbox example
return (
<form onSubmit={onSubmit}>
<WrapperInput
name="controllerInput"
label="This input uses Controller:"
type="text"
rules={{ required: "You must enter something" }}
defaultValue=""
/>
<button
type="button"
onClick={() => {
// getValues() works if use following setValue()
const testSetVal = setValue("controllerInput", "Hopper", {
shouldValidate: true,
shouldDirty: true
});
// testGetVal returns undefined if setValue() is removed
const testGetVal = getValues("controllerInput");
console.log(testGetVal);
}}
>
GET VALUES
</button>
<button type="submit">Submit</button>
</form>
);
More info:
I don't understand why getValues() is returning undefined. When I view the control object in React dev tools I can see the value is saved. I get undefined on form submission too.
My general approach here is to use an atomic Input.tsx component that will handle an input styling. Then I use a WrapperInput.tsx to turn it into a controlled input using react-hook-fom.
Lift the control to the parent instead and pass it to the reusable component as prop.
// RegistrationForm.tsx
...
const {
setValue,
getValues,
handleSubmit,
formState: { errors },
control,
} = useForm();
...
return (
...
<WrapperInput
control={control} // pass it here
name="controllerInput"
label="This input uses Controller:"
type="text"
rules={{ required: "You must enter something" }}
defaultValue=""
/>
)
// WrapperInput.tsx
const WrapperInput: FC<InputProps> = ({
name,
rules,
label,
onChange,
control, /* use control from parent instead */
defaultValue,
...props
}) => {
return (
<div>
<Controller
control={control}
name={name}
defaultValue={defaultValue}
render={({ field }) => <Input label={label} {...field} />}
/>
</div>
);
};
Codesandbox

Set checkbox checked state of checkbox group programmatically in Formik

I have a checkbox group (checkboxes with the same name), that I'd like to toggle them through an external button.
The parent component will map over the data and create multiple checkboxes given the data, like so:
(options.map(
option => (
<Field
name={field.name}
value={option.value}
component={CheckBoxButton}
/>
)
)}
And the CheckBoxButton is
const CheckBoxButton = ({
value,
field,
...props
}) => {
const [activeState, setActiveState] = useState(false)
return (
<div>
<Field
type="checkbox"
{...field}
{...props}
value={value}
checked={activeState}
/>
<Button
active={activeState}
onClick={() => {
setActiveState(!activeState)
}}
>
Toggle Checkbox
</Button>
</div>
)
}
Now on submission, it seems the checked={activeState} value passed to the field seemed to be ignored, nor that it triggers onChange when changed. I have tried to use setFieldValue() but since we have multiple checkboxes with the same name, it only captures the last value. To use it means I'd have to manually structure the value array and pop/add values, which seems like an overkill.
I also tried using useRef and setting ref.current.checked manually, but at no use.
Any insights on what I might be doing wrong, or how to properly set the value programmatically?
So, I managed to get a workaround that solves the problem by wrapping the button (now a div) and checkbox within the same Label, and overriding the state within the onChange event of the field. Each field would have a unique identifier id which the label binds to using htmlFor.
While a valid workaround, it would still be nice to have a programmatic way to do so.
const CheckBoxButton = ({value,field,...props}) => {
const [activeState, setActiveState] = useState(active)
const { handleChange } = useFormikContext()
const fieldId = `${field.name}-${value}`
return (
<label htmlFor={fieldId}>
<Field
id={fieldId}
type="checkbox"
{...field}
{...props}
value={value}
onChange={(event) => {
setActiveState(!activeState)
handleChange(event)
}}
className={style.checkbox}
/>
<span active={activeState} >
Toggle Checkbox
</span>
</label>
)
}

Is there access to Formik props inside <Field validate>

I have 2 fields. Second field should be auto populated once input of first field is validated OK. In order to populate second field i need to access formik's helper methods, which we normally have inside <Field>.
Portion of code related to formik:
const validateRoutingNumber = (value: string) => {
// props.form.setFieldValue('financialInstitution', 'ANYTHING'); <---- Can do something like this?
return;
}
<Formik initialValues={initialValues} validationSchema={Schema} validateOnMount onSubmit={submitForm}>
<Form>
<Field name="routingNumber" validate={validateRoutingNumber}>
{(props: FieldProps) => (
<TextField
variant="outlined"
{...props.field}
{...textErrors(props.meta)}
type="text"
inputProps={{'aria-label': t('routingNumber')}}
/>
)}
</Field>
Is there a way to access to props inside <Field>, like we do inside <TextField>?
I found out that i can access the desired formik properties. In similar manner how access is provided to formik properties on <TextField> level, access can be provided to <Formik> level using render props tehnique.
Portion of code to provide desired behavior:
const validateRoutingNumber = async (value: string, formikProps: FormikProps<any>) => {
console.log('formikProps', formikProps);
....
}
<Formik initialValues={initialValues} validationSchema={Schema} validateOnMount onSubmit={submitForm}>
{(formikProps: FormikProps<any>) => (
<Form>
<Field name="routingNumber" validate={(value: string) => validateRoutingNumber(value, formikProps)}>
{(props: FieldProps) => (
<TextField
There are nice video materials on youtube regarding formik usage - https://www.youtube.com/watch?v=0TFIBPyitXo

How to handle Formik's `handleChange` prop?

I get it that Field has an onChange attribute where you can pass the own Formik onChange prop from here: https://jaredpalmer.com/formik/docs/api/formik#handlechange-e-reactchangeevent-any-void.
However, I am struggling to understand where these value[key] is passed, so I can handle the data passed in the form. Found in withFormik(): How to use handleChange that I can pass two callbacks to Formik's onChange prop, but I wonder if there is a better way to handle this.
edit after comments from folks that replied, thanks for that:
My code using these 2 callbacks in the onChange prop in Field:
export default function FormikForm() {
const onSubmitHandler = (formValues, actions) => {
console.log(formValues);
console.log(actions);
};
const onChangeHandler = (e) => {
console.log(e.target.value);
};
return (
<div>
<h1>This is the Formik Form</h1>
<Formik
initialValues={{
name: "",
email: "",
age: ""
}}
onSubmit={onSubmitHandler}
render={props => {
return (
<Form>
<label>
Name
<Field
name="name"
type="text"
placeholder="name"
onChange={e => {props.handleChange(e); onChangeHandler(e)}}
/>
</label>
<button type="submit">Submit</button>
</Form>
);
}}
/>
</div>
);
}
Is there a way to do a similar thing as in onSubmitHandler, where Formik automagically outputs the value of the input without having to call these 2 functions in the onChange?
Thanks
Every field component receives a field prop which has a value prop containing the value for that field, as well as a form prop containing helper methods that you can use to set the value. I'd need to see the structure of your code to give specific suggestions on how to implement what you want, but you can emulate the default functionality by calling form.setFieldValue(field.name, field.value). In addition, the field prop has this handler built in by default in the field.onChange prop.

Resources