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
Related
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.
This is my first time posting and I am a junior Frontend developer so please go easy on me.
This has me very confused and I've looked, tested and for some reason the Inputlabel from Material UI (ver. 5) is not working on this side of the website I am developing. I used inputlabel and date picker on the home page and they work with no problems. But suddenly trying to make a user profile form... Well, it's starting to give me trouble. Literally copying what is in the documentation and still get this issue.
When I click on the Input everythig on the page disappears. Opening the Chrome Inspector gives me the error that's in the title. And I am truly clueless. Posting my code to see if someone cand lend me a bit of help here. Thank you in advance.
import React from 'react';
import { TextField } from '#mui/material';
import { Grid } from '#mui/material';
import { Box } from '#mui/system';
import { Container } from '#mui/material';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormControl from '#mui/material/FormControl';
import Select from '#mui/material/Select';
import { DatePicker } from "#mui/lab";
import { LocalizationProvider } from "#mui/lab";
import AdapterDateFns from '#mui/lab/AdapterDateFns';
//import '../User/components/styles/UserStyles.scss';
const UserProfileTxt = () => {
//DOB picker
const [dob, setDob] = React.useState(new Date())
//Nationality Picker
const [nationality, setNationality] = React.useState('');
const handleChange = (event) => {
setNationality(event.target.value);
};
return (
<Container>
<Grid container>
<Grid sx={{ flexDirection: 'column', display: 'flex' }}>
<TextField sx={{mt:'0.5rem', mb:'0.5rem'}} variant='outlined' label='Nombre' />
<TextField sx={{mt:'0.5rem', mb:'0.5rem'}} variant='outlined' label='Apellido' />
<TextField sx={{mt:'0.5rem', mb:'0.5rem'}}variant='outlined' label='E-mail' />
<Box sx={{ minWidth: 120, mt:'0.5rem', mb:'1rem' }}>
<FormControl fullWidth>
<InputLabel id="NationalityUSERHIKLUB">Nacionalidad</InputLabel>
<Select
labelId="Nacionalidad"
id="Naccionalidad"
value={nationality}
label="Nationalidad"
onChange={handleChange}
>
<MenuItem value={1}>Ten</MenuItem>
</Select>
</FormControl>
</Box>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Seleccióna fecha de Nacimiento"
value={dob}
onChange={(newValue) => {
setDob(newValue);
}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
</Grid>
<Grid>
</Grid>
</Grid>
</Container>
);
};
export default UserProfileTxt;
Okay. I solved my own question.
This is probably a bug, but I didn't add this to the original question because I seriously didn't think it was going to be related. Despite that, I am going to share it in case someone is having issues as well.
So, this form I am doing is inside a <Tabs/> component, which I costumized using <ThemeProvider/> and <CreateTheme/>. Started basically breaking my code down seeing where the error could stem from. Finally, I noticed that it could be the <ThemeProvider/> on the tabs. And sure enough, once I removed both <ThemeProvider/> and <CreateTheme/> The date picker and dropdown menu were working fine.
Sorry I didn't address it before but I seriously did not think it was going to be related to the issue at hand
So, long story short:
DO NOT USE THEMEPROVIDER WHEN USING TABS ON MUI VER 5. USE STYLED COMPONENTS INSTEAD AND GET THE SAME RESULT WITH A LOT LESS ISSUES. I suppose there is some sort of conflict with that and the tabs component.
Thanks to anyone who answered.
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 using a RadioGroup component to display a dynamic list of Radio options using FormControlLabel. The dynamic radio options are successfully getting displayed and I'm able to retrieve the selected radio option using onChange in RadioGroup. However, when I check a radio option, that particular option does not appear "checked".
Here's the code I'm using:
export default class Book extends Component {
state = {
slot: null,
};
...
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
...
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">Select availability slot</FormLabel>
<RadioGroup
aria-label="Slot"
name="slot"
className={classes.group}
value={this.state.slot}
onChange={this.onChange}
>
{this.props.experience.availability !== null ? (
this.props.experience.availability.map(single => (
<FormControlLabel
key={single.id}
value={single.id}
control={<Radio color="primary" />}
label={single.title}
/>
))
) : ""}
// Some manual options in addition to the above dynamic list
<FormControlLabel
value="female"
control={<Radio color="primary" />}
label="Female"
/>
<FormControlLabel
value="male"
control={<Radio color="primary" />}
label="Male"
/>
</RadioGroup>
</FormControl>
...
}
this.props.experience.availability is an array of objects that I'm getting from a call I'm making to the backend. The call is being made in componentDidMount(). I use Redux, which makes the result available as a prop.
Now, if I manually add a few FormControlLabel components in the same RadioGroup, I can see that it's checked after selecting that option.
I've taken two screenshots - this is when the manual FormControlLabel is selected: https://ibb.co/pL5Fy6L and this is when one of my dynamic option is selected: https://ibb.co/Jq7mLN4
You can see that in the second one, the "Female" option gets unchecked but the option that I selected (20-06-2019) does not appear to be checked.
Can you please help me fix this?
Thanks in advance!
For anybody who might be facing the same issue, here's what I was doing wrong.
The problem was that I was passing an integer value to the value prop instead of a string like: value={single.id} so I added toString() to convert it to string like this: value={single.id.toString()} and it's working fine now.
I am trying to use Formik with Material-UI text field. Like so:
import TextField from '#material-ui/core/TextField';
import {
Field,
FieldProps,
Form,
Formik,
FormikErrors,
FormikProps
} from 'formik';
import React, { Component } from 'react';
interface IMyFormValues {
firstName: string;
}
class CreateAgreementForm extends Component<{}> {
public render() {
return (
<div>
<h1>My Example</h1>
<Formik
initialValues={{ firstName: '' }}
// tslint:disable-next-line:jsx-no-lambda
onSubmit={(values: IMyFormValues) => alert(JSON.stringify(values))}
// tslint:disable-next-line:jsx-no-lambda
validate={(values: IMyFormValues) => {
const errors: FormikErrors<IMyFormValues> = {};
if (!values.firstName) {
errors.firstName = 'Required';
}
return errors;
}}
// tslint:disable-next-line:jsx-no-lambda
render={(formikBag: FormikProps<IMyFormValues>) => (
<Form>
<Field
name="firstName"
render={({ field, form }: FieldProps<IMyFormValues>) => (
<TextField
error={Boolean(
form.errors.firstName && form.touched.firstName
)}
helperText={
form.errors.firstName &&
form.touched.firstName &&
String(form.errors.firstName)
}
/>
)}
/>
</Form>
)}
/>
</div>
);
}
}
export default CreateAgreementForm;
I want Formik to be responsible for validation and Material-UI for looks.
I want to pass errors.firstName to TextField component but the error doesn't display correctly. How can I fix it so it still will be clear to read? I don't want to write my own TextField component.
I don't think you need another library or even create your own wrapper, I think you need to tweek your code a bit.
One problem you have is that you don't pass an onChange function in the Material TextField so the form value of firstName is always null and so you always get the error, even if you have entered a name.
Try adding a name or id on your TextField and an onChange function like so:
<Field
validateOnBlur
validateOnChange
name="firstName"
render={({ field, form }) => (
<TextField
name={"firstName"}
error={
Boolean(form.errors.firstName && form.touched.firstName)
}
onChange={formikBag.handleChange}
onBlur={formikBag.handleBlur}
helperText={
form.errors.firstName &&
form.touched.firstName &&
String(form.errors.firstName)
}
/>
)}
/>
As mentionned in comments, it may actually be a good idea to implement "wrapper" components, like they did in this samples from Formik or ReactFinalForm :
https://github.com/stackworx/formik-material-ui/tree/master/src
https://github.com/final-form/react-final-form#material-ui-10
The idea is the same : implement custom "wrapper" components to wrap Material-UI components and map Formik or ReactFinalForm APIs props.
The advantages of this approach is to centralize in one place the mapping between the two frameworks, so that you do not repeat the mapping each time, and if one of the framework introduces breaking changes you just have to change those custom "wrapper" components.
You can try this: https://github.com/daixianceng/formik-material-fields
Installation:
npm install --save formik-material-fields
Usage:
import React, { Component } from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import { FormikTextField } from 'formik-material-fields';
const validationSchema = Yup.object().shape({
username: Yup.string().required(),
});
const initialValues = {
username: '',
};
class MyForm extends Component {
render() {
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={this.props.onSubmit}
>
{({ isValid }) => (
<Form autoComplete="off">
<FormikTextField
name="username"
label="Username"
margin="normal"
fullWidth
/>
</Form>
)}
</Formik>
);
}
}
Check out the formik docs for <Field /> here: https://jaredpalmer.com/formik/docs/api/field
As an example you could use Material's OutlinedInput to style your input:
<Field as={OutlinedInput} />
If you need to pass more props to OutlinedInput, simply add them to Field and it will pass them on to OutlinedInput:
<Field as={OutlinedInput} color="primary" InputProps={{ startAdornment: <InputAdornment position="start"><AccountCircle /></InputAdornment> }} />
You can use setFieldValue method useful for creating custom input change handlers
<Formik
initialValues={{
name: "",
}}
onSubmit={(values: any) => console.log(values)}
>
{({ handleSubmit, setFieldValue }) => (
<Form noValidate autoComplete="off" onSubmit={handleSubmit}>
<TextField
onChange={(event) => setFieldValue("name", event.target.value)}
type="text"
label="Name"
/>
</Form>
)}
</Formik>
To use material-ui and formik, you can use the variant from the official formik documentation:
https://formik.org/docs/examples/with-material-ui
You could also try this library, which does the heavy-lifting for you and implements the wrapper code around Material-UI components (including <TextField />): https://github.com/stackworx/formik-material-ui.
Installation:
yarn add formik-material-ui
In your Formik form component, pass the <TextField /> component as the component prop of the Formik <Field /> component.
import { Formik, Field, Form } from 'formik';
import { TextField } from 'formik-material-ui';
<Field
name="email"
label="Email"
type="email"
component={TextField}
/>
Formik will continue to handle the validation as expected and will render the Material UI component and error message. There are additional details in the docs for other Mui input components and to aid with customization.