I have a dropdown where a user can choose from "Item 1", "Item 2" and "Item 3". There is also a "Reset" button that lets the user clear their choice to make a new one.
Here's my code :
import React from "react";
import { Button, FormControl, MenuItem, Select } from "#mui/material";
import { Controller, useForm, FormProvider } from "react-hook-form";
export default function App() {
const methods = useForm();
const { control, resetField } = methods;
const dropDownItems = ["Item 1", "Item 2", "Item 3"];
return (
<FormProvider {...methods}>
<form>
<FormControl sx={{ width: "51%" }}>
<Controller
name="firstItem"
control={control}
rules={{ required: true }}
render={({ field }) => (
<Select {...field} id="firstItem">
{dropDownItems.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
</form>
<Button onClick={() => resetField("firstItem")}>Reset</Button>
</FormProvider>
);
}
I expect the resetField to empty the choice of the dropdown on clicking the reset button. And it does so, but only in the state of the form (and the display does not change). I'm assuming there is something about control that I do not quite understand, but an afternoon of looking around did not let me find the answer.
I also tried using setValue instead of resetField, to no avail.
Link: https://codesandbox.io/s/blue-platform-74tgg8?file=/src/App.js
What am I missing? Thanks for the help :)
Edit: In my use-case, this is part of a bigger form, so using reset to reset the fields entirely does not work for me.
Related
I am creating a React form using Material UI components. I have an empty array questionAns that I am using to keep track of the responses. I want to add an element, say yes to the array when the Yes radio button is selected on the form. Here is a subset of the form I am building:
import * as React from "react";
import TextField from "#mui/material/TextField";
import {
Button,
Radio,
RadioGroup,
FormControlLabel,
FormControl,
FormLabel,
FormHelperText,
FormGroup,
Checkbox,
Grid,
Box,
} from "#mui/material";
...
<FormControl error={Boolean(errors.question1)}>
<FormLabel component="legend">
{Records[0].question}
</FormLabel>
<RadioGroup row aria-label="question1" name="question1">
<FormControlLabel
value="Yes"
control={
<Radio
{...register("awQuestion1", {
required: "Please select a response.",
})}
/>
}
label="Yes"
/>
<FormControlLabel
value="No"
control={
<Radio
{...register("awQuestion1", {
required: "Please select a response.",
})}
/>
}
label="No"
/>
</RadioGroup>
<FormHelperText style={{ color: "#d32f2f" }}>
{errors.question1?.message}
</FormHelperText>
</FormControl>
How can I incorporate some sort of write function to accommodate my desired functionality? I'm not very familiar with JavaScript and React, but I would like to incorporate this hopefully simple feature.
I have a series of questions in the same structure as above.
Thank you.
You need to use a hook - useState - to keep track of the "Yes" answers, for example at the top of your component declare an answersArray state and its setter function:
const [answersArray, setAnswersArray] = useState([]);
Then on the radio buttons add an onChange prop
onChange={(e)=>{
if(e.checked) setAnswersArray((prevState)=> [...prevState, "Yes"]) //"No" on the no button
}
This is just the jist check out the docs for a better tutorial
https://beta.reactjs.org/reference/react/useState
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;
I am trying to use the radio buttons from material-ui - https://material-ui.com/components/radio-buttons/
Although the example here doesn't provide the selected radio button on submitting the form. I am also trying to use redux-forms - https://redux-form.com/6.0.0-rc.4/examples/material-ui/ but their documentation seems out of date. I am not sure how you would pre-select a radio button for example.
renderRadioGroup.js
import React from 'react';
import Radio from '#material-ui/core/Radio';
import RadioGroup from '#material-ui/core/RadioGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
const renderRadioGroup = ({ input, label, value, fieldName, handleChange, options, meta: { touched, error }}) => (
<FormControl component="fieldset" fullWidth={true}>
<FormLabel component="legend">{label} {value}</FormLabel>
<RadioGroup
row
aria-label={fieldName}
name={fieldName}
value={value}
onChange={handleChange}
>
{
options.map((item, j) =>
<FormControlLabel key={j} value={item.value} disabled={item.disabled} control={<Radio />} label={item.label} />
)
}
</RadioGroup>
</FormControl>
)
export default renderRadioGroup;
shell
<Field
name="paymentplan"
component={renderRadioGroup}
label="Do you wish to pay monthly or annually?"
value={"1"}
fieldName="paymentplan"
options={
[
{
"label" : "Monthly",
"value" : "0"
},
{
"label" : "Annually",
"value" : "1"
}
]
}
/>
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.
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>
);
}