How to use custom radio component with react-final-form? - reactjs

I am trying to use a custom Radio component with React-final-form but it is not acting as a radio button but as a checkbox, ie, all the buttons are open for selection.
The 3rd party Radio button has the following schema:
checked boolean
Whether or not radio is checked
onChange () => void
Called when the user attempts to change the checked state
name string
The input name, used to reference the element in JavaScript
I created a custom Component for using the Radio Component:
const CustomRadio = (props: any) => (
<Radio
{...props.input}
{...props.rest}
name={props.name}
onChange={() => props.input.onChange()}
/>
)
and I am using it as follows:
<Field name="food"
component={CustomRadio}
value="1"
/>
<Field name="food"
component={CustomRadio}
value="2"
/>
Being very new to RFF and new to React, I may be doing something very wrong, hence any help will be appreciated.
Basically, I want to use RFF with my 3rd party components. Though I have been successful to use my Input component with RFF as expected, Radio Button is the one creating problems.

Here is the correct implementation for Radio with react-final-form:-
https://codesandbox.io/s/vibrant-easley-5n1ek?file=/index.js
/* eslint-disable jsx-a11y/accessible-emoji */
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";
import RadioGroup from "#material-ui/core/RadioGroup";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import FormControl from "#material-ui/core/FormControl";
import Radio from "#material-ui/core/Radio";
import FormLabel from "#material-ui/core/FormLabel";
const RadioWrapper = (props) => {
const {
input: { checked, value, name, onChange, ...restInput },
} = props;
return (
<Radio
name={name}
inputProps={restInput}
onChange={onChange}
checked={checked}
value={value}
/>
);
};
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const onSubmit = async (values) => {
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const App = () => {
return (
<Styles>
<h1>React Final Form - Simple Example</h1>
<a
href="https://final-form.org/react"
target="_blank"
rel="noopener noreferrer"
>
Read Docs
</a>
<Form
onSubmit={onSubmit}
initialValues={{
employed: false,
all_sub_tenants: "true"
}}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<FormControl component="fieldset">
<FormLabel component="legend">
Select Tenants
</FormLabel>
<RadioGroup aria-label="allSubTenants" name="allSubTenants">
<FormControlLabel
value="true"
control={
<Field
name="all_sub_tenants"
component={RadioWrapper}
type="radio"
value={"true"}
/>
}
label="All Sub-Tenants"
/>
<FormControlLabel
value="false"
control={
<Field
name="all_sub_tenants"
component={RadioWrapper}
type="radio"
value={"false"}
/>
}
label="Select Sub-Tenants"
/>
</RadioGroup>
</FormControl>
<div>
<label>Notes</label>
<Field name="notes" component="textarea" placeholder="Notes" />
</div>
<div className="buttons">
<button type="submit" disabled={submitting || pristine}>
Submit
</button>
<button
type="button"
onClick={form.reset}
disabled={submitting || pristine}
>
Reset
</button>
</div>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
</Styles>
);
};
render(<App />, document.getElementById("root"));

Related

Formik Fetch API Values Undefined on submit

I am new to react and I need help with submitting my form with values that was obtained from an API.
When the form is loaded, the user has to input an ID in order for it to load the remaining field and values from the API. Once the User has inserted an ID value, the user can then click on submit and the in the back end it should POST the results capture in the Console log. Currently on submit input values are Undefined.
Visit CodeSandbox link below for a working example of this
Code: https://codesandbox.io/s/formik-fetch-post-3remet
Alternatively, here is my Code:
import React, { useState, useEffect } from "react";
import "./styles.css";
import { Box, Button, TextField } from "#mui/material";
import { Formik, Form } from "formik";
export default function App() {
const [datas, setdatas] = useState([]);
const [searchId, setSearchId] = useState("");
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/users/?id=${searchId}`)
.then((Response) => Response.json())
.then((datas) => setdatas(datas));
}, [searchId]);
const handleCange = (e) => {
setSearchId(e.target.value);
};
return (
<Formik
initialValues={{ name: datas.name }}
enableReinitialize={true}
onSubmit={(data, { resetForm }) => {
console.log(data);
resetForm();
}}
>
<div className="App">
<h1>Search User(enter a value between 1-5)</h1>
<div className="searchBox">
<input
type="text"
placeholder="Enter user ID"
onChange={(e) => handleCange(e)}
/>
</div>
<div className="itemsSec">
{datas.map((datas) => (
<div key={datas.id} className="items">
<Form>
<Box>
<TextField
className="field"
label="name"
name="name"
type="text"
id="name"
variant="filled"
value={datas.name}
onBlur={Formik.handleBlur}
onChange={Formik.handleChange}
sx={{ gridColumn: "span 2" }}
key={datas.id}
>
{" "}
value={datas.name}
</TextField>
</Box>
<Button type="submit" color="secondary" variant="contained">
Submit
</Button>
</Form>
</div>
))}
</div>
</div>
</Formik>
);
}

#mui/material <Checkbox> with Formik

I wish to build a React (multi step) form using Formik and Mui. I cannot understand how to bind/control the mui checkbox element
import { Button, Checkbox, Typography } from "#mui/material";
import { Field, Form, Formik } from "formik";
import "./styles.css";
export default function App() {
var fruitValues = ["apple", "banana"];
function handleSubmit(values, actions) {
fruitValues = values.fruit;
console.debug(values.fruit);
}
return (
<div className="App">
<Formik initialValues={{ fruit: ["apple"] }} onSubmit={handleSubmit}>
<Form id="test2">
<Checkbox name="fruit" value="apple" label="Apple" />
<Checkbox name="fruit" value="banana" label="Banana" />
<Checkbox name="fruit" value="orange" label="Orange" />
<Button type="submit">Submit</Button>
</Form>
</Formik>
</div>
);
}
See https://codesandbox.io/s/thirsty-wing-91glso?file=/src/App.js:0-1380
I am working on this as well. Seems like something that should be covered in the docs but just isn't.
There is this example. It covers doing something like this ...
<Form>
<Field
type="email"
name="email"
component={TextField}
color={"error"}
/>
But there isn't any explanation of where this comes from or what is happening here.
My reaction is a little late but I faced the same issue and made it work as follow.
import React from 'react'
import { Field, Form, Formik } from 'formik'
import Checkbox from '#mui/material/Checkbox'
import FormControlLabel from '#mui/material/FormControlLabel'
export default function Example() {
return (
<Formik>
<Form>
<Field name="terms_of_condition">
{({ field }) => (
<FormControlLabel
onChange={field.onChange}
control={<Checkbox name="terms_of_condition" />}
label="Terms of conditions"
sx={sx}
value="on"
/>
)}
</Field>
</Form>
</Formik>
)
}

Dynamically change value in Formik when a state changes

I need to change the value in my form fields every time a state (called selectedProduct, its input field is not included in Formik tag) changes.
I tried:
putting the value I want in the initialValues (does not work obviously because at the first render I haven't choosen my selectedProduct yet)
putting the value I want in the "value" props in every field in formik.
It almost work: Datepicker gets the right value, select box input does not get any value(idk why), text field take the right value but this is covered by the label. And that's because none of the fields get validated.
This is what I've done, with the two points above applied
import React, { useState } from 'react';
import * as Yup from 'yup';
import {
Formik, Form, ErrorMessage, Field,
} from 'formik';
import {
Button, TextField,
MenuItem,
} from '#material-ui/core';
import DateFnsUtils from '#date-io/date-fns';
import {
MuiPickersUtilsProvider,
KeyboardDatePicker,
} from '#material-ui/pickers';
const validationSchema = Yup.object().shape({
startValidityDate: Yup.date().required(),
discount: Yup.string().required(),
days: Yup.string().required(),
});
const MyComponent = ({filteredProduct, daysList}) => {
const [selectedProduct, setSelectedProduct] = useState('');
const onChangeProduct = (product) => {
setSelectedProduct(product.target.value);
};
const handleRunButton = (newExecution) => {
console.log(newExecution);
};
return (
<div>
<div className={classes.productComboWrapper}>
<div id="selectBoxNotIncludedInFormik">
<TextField
margin="normal"
style={{}}
variant="outlined"
name="productId"
id="productId"
fullWidth
select
label="Select product"
value={selectedProduct?.id}
onChange={(product) => onChangeProduct(product)}
>
<MenuItem key="" value="">
{StringsConst.noneSelected}
</MenuItem>
{filteredProduct?.map((el) => (
<MenuItem key={el} value={el}>
{el.isin}
</MenuItem>
))}
</TextField>
</div>
</div>
<Formik
initialValues={{
startValidityDate: selectedProduct?.startValidityDate,
days: selectedProduct?.coupon?.days,
discount: selectedProduct?.discount,
}}
validationSchema={validationSchema}
onSubmit={(values) => {
const newExecution = {
startValidityDate: values.startValidityDate,
days: values.days,
discount: values.discount,
};
handleRunButton(newExecution);
}}
>
{({
errors, dirty, setFieldValue, values,
}) => (
<Form>
<div className={classes.datePicker}>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
disableToolbar
label="Start Validity Date"
inputVariant="outlined"
variant="inline"
autoOk
fullWidth
disabled
format="dd/MM/yyyy"
value={selectedProduct?.startValidityDate}
onChange={(dt) => setFieldValue('startValidityDate', dt)}
KeyboardButtonProps={{
'aria-label': 'change date',
}}
/>
</MuiPickersUtilsProvider>
</div>
<div className={classes.fieldWrapper}>
<Field
className={classes.field}
name="discount"
as={TextField}
variant="outlined"
margin="normal"
fullWidth
id="discount"
autoComplete="discount"
placeholder="Discount"
disabled
value={selectedProduct?.discount}
/>
</div>
<div className={classes.textFieldWrapper}>
<TextField
margin="normal"
style={{}}
variant="outlined"
name="days"
id="days"
fullWidth
select
label="Days"
disabled
value={selectedProduct?.coupon?.days}
onChange={(val) => setFieldValue('days', val.target.value)}
>
<MenuItem key="" value="">
{StringsConst.noneSelected}
</MenuItem>
{daysList.map((el) => (
<MenuItem key={el} value={el}>
{el}
</MenuItem>
))}
</TextField>
</div>
<div className={classes.buttonContainer}>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
{StringsConst.Run}
</Button>
</div>
</Form>
)}
</Formik>
</div>
)
}
So, the three input fields in the form are disabled, but I need them to fill when I choose a value in the first select box outside of the form.
Can you suggest me another approach?
You can connect input or button to the form outside the form.
like this code:
<form id="myForm">
<button> click me </button>
</form>
<input type="text" form="myForm"/>
ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefform

React - Material-UI - How to use Select with multiple values inside react-hook-form

I am trying to use UI-Material Select with multiple options inside react-hook-form without success.
I had this working before trying to do it with multiple options
<form onSubmit={handleSubmit(onSubmit)}>
<Row className="mb-2">
<Col sm={6}>
<FormControl className="select-language">
<InputLabel> {t('Languages')}</InputLabel>
<Controller
as={
<Select>
{Config.languages.map(m => <MenuItem key={m.code} value={m.text}> {t(m.text)} </MenuItem>)}
</Select>
}
defaultValue={user.language}
name="language"
control={control}
>
</Controller>
</FormControl>
</Col>
</Row>
</form>
I tried to add multiple to the Select element which lead me to another error.
I tried also to keep only the Select element without the Controller wrapper, but then I can't get the language value in onSubmit
Very simple codeSandBox that shows that I can't get value from Select when submitting the form: https://codesandbox.io/s/react-hook-form-example-s7h5p?file=/src/index.js
I would appreciate any help
Thanks
If anyone looking for a easy solution, this might come in handy. Multiple select options become very easy now with Select Component. If you look at the Select component, you just have to set the default value to an array and pass the "multiple" prop.
import {
Button,
FormControl,
InputLabel,
MenuItem,
Select
} from "#mui/material";
import { Controller, useForm } from "react-hook-form";
const FCWidth = {
width: "20rem"
};
export default function App() {
const { control, handleSubmit } = useForm();
const formSubmitHandler = (formData) => {
console.log(formData);
};
const ages = ["10", "20", "30"];
return (
<div className="App">
<form onSubmit={handleSubmit(formSubmitHandler)}>
<Controller
name="age"
control={control}
type="text"
defaultValue={[]}
render={({ field }) => (
<FormControl sx={FCWidth}>
<InputLabel id="age">Age</InputLabel>
<Select
{...field}
labelId="age"
label="age"
multiple
defaultValue={[]}
>
{ages.map((age) => (
<MenuItem value={age} key={age}>
{age}
</MenuItem>
))}
</Select>
</FormControl>
)}
/>
<FormControl sx={FCWidth}>
<Button
type="submit"
variant="contained"
fullWidth
sx={{ marginTop: ".75rem", fontWeight: "bold" }}
>
Submit
</Button>
</FormControl>
</form>
</div>
);
}
Here is the code sandbox link https://codesandbox.io/s/select-multiple-option-with-mui-and-react-hook-form-2kv2o
I know that this old but someone may need it later, maybe this one would work for you,
just check the render Controller props
import React from "react";
import {
TextField,
ListItemText,
Checkbox,
MenuItem,
makeStyles
} from "#material-ui/core";
import { useForm, Controller } from "react-hook-form";
const useStyles = makeStyles({
root: {
width: "200px"
},
center: {
textAlign: "center"
}
});
export default function SelectTextField() {
const classes = useStyles();
const { handleSubmit, control } = useForm();
const nums = [1, 2, 3, 4];
const onSubmit = (e) => {
console.log(e);
};
return (
<form className={classes.center} onSubmit={handleSubmit(onSubmit)}>
<Controller
control={control}
name="selectedNumbers"
defaultValue={[]}
render={({ onChange, value }) => {
console.log(value);
return (
<TextField
classes={{ root: classes.root }}
select
id="Numbers"
variant="outlined"
label="Numbers"
SelectProps={{
multiple: true,
value: value,
renderValue: (selected) => selected.join(", "),
onChange: onChange
}}
>
{nums.map((n) => (
<MenuItem key={n} value={n}>
<Checkbox checked={value.includes(n)} />
<ListItemText primary={n} />
</MenuItem>
))}
</TextField>
);
}}
/>
<div>
<button type="submit">Submit</button>
</div>
</form>
);
}
also, you can and play with it here
https://codesandbox.io/s/multi-select-input-u0cr3?file=/demo.js
also can check this on the docs
https://react-hook-form.com/get-started#ReactNative
First of all, i think your version is on v3, so you may want to upgrade:
https://codesandbox.io/s/react-hook-form-example-5lrij?file=/src/index.js
import React from "react";
import ReactDOM from "react-dom";
import { useForm, Controller } from "react-hook-form";
import { Select, MenuItem } from "#material-ui/core";
import "./styles.css";
const languages = [
{
code: "en-US",
text: "English"
},
{
code: "zu",
text: "Another Language"
}
];
export default function App() {
const { register, handleSubmit, control } = useForm();
const onSubmit = data => console.log(data);
return (
<div className="container">
<div className="col-sm-12">
<h3>Client Profile</h3>
</div>
<div className="col-sm-12">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group">
<input
className="form-control"
type="text"
placeholder="Name"
name="Name"
ref={register}
/>
</div>
<div className="form-group">
<Controller
name="Language"
control={control}
as={
<Select>
{languages.map(m => (
<MenuItem key={m.code} value={m.text}>
{" "}
{m.text}{" "}
</MenuItem>
))}
</Select>
}
defaultValue={languages[0].text}
/>
</div>
<div className="form-group">
<input className="btn btn-primary" type="submit" />
</div>
</form>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
here is the fixed version by using Controller

Checkbox onChange event is not handled by handleChange props by Formik

I was building simple Form using React and Formik library.
I have added check box inside the form tag which is wrapped by withFormik wrapper of formik library.
I have tried to changing from
<input
type="checkbox"
name="flag"
checked={values.flag}
onChange={handleChange}
onBlur={handleBlur}
/>
to
<input
type="checkbox"
name="flag"
value={values.flag}
onChange={handleChange}
onBlur={handleBlur}
/>
but none is working.
the component is as following
import { withFormik } from 'formik';
...
const Form = props => (
<form>
<input
type="checkbox"
name="flag"
checked={props.values.flag}
onChange={props.handleChange}
onBlur={props.handleBlur}
/>
<input
type="text"
name="name"
checked={props.values.name}
onChange={props.handleChange}
onBlur={props.handleBlur}
/>
</form>
);
const WrappedForm = withFormik({
displayName: 'BasicForm',
})(Form);
export default WrappedForm;
It should change props.values when clicking checkbox.
but it doesn't change props data at all.
Btw, it changes props data when typing in text input box.
This only happens with checkbox.
Using the setFieldValue from Formik props, you can set the value of the check to true or false.
<CheckBox
checked={values.check}
onPress={() => setFieldValue('check', !values.check)}
/>
My answer relates to react-native checkbox.
This article is very helpful. https://heartbeat.fritz.ai/handling-different-field-types-in-react-native-forms-with-formik-and-yup-fa9ea89d867e
Im using react material ui library, here is how i manage my checkboxes :
import { FormControlLabel, Checkbox} from "#material-ui/core";
import { Formik, Form, Field } from "formik";
<Formik
initialValues={{
check: false
}}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({ values, setFieldValue }) => (
<Form className="gt-form">
<FormControlLabel
checked={values.check}
onChange={() => setFieldValue("check", !values.check)}
control={<Checkbox />}
label="save this for later"
/>
</Form>
)}
</Formik>

Resources