get field value from Formik & Material UI form - javascript

I am trying to disable a checkbox group based on the value of a radio group. I followed the method used in the last part of the Formik tutorial. Using react context removes a lot of clutter from the form itself but I'm not sure how to expose some values now.
In the form below, in the CheckboxGroup component, I'm attempting to print the word disabled as an attribute of checkbox1 if radio4's value is "yes". I'm not sure what value should be used here as fields doesn't work. How do I pass a value to the form given the React Context method used?
The form:
export default function HealthAssessmentForm() {
return (
<Formik
initialValues={{
radio4: '',
symptoms: '',
}}
onSubmit={async (values) => {
await new Promise((r) => setTimeout(r, 500));
console.log(JSON.stringify(values, null, 2));
}}
validator={() => ({})}
>
<Form>
<RadioInputGroup
label="Disable the checkbox?"
name="radio4"
options={['Yes','No']}
/>
<CheckboxGroup
{(fields.radio4.value === "yes") ? "disabled" : null}
name="checkbox1"
options={[
{name:"hello",label:"hello"},
{name:"there",label:"there"},
]}
/>
<button type="submit">Submit</button>
</Form>
</Formik>
)
}
I'm not sure the custom components are relevant here but...
const RadioInputGroup = (props) => {
const [field, meta] = useField({...props, type:'radio'});
return (
<FormControl component="fieldset">
<FormLabel component="legend">{props.label}</FormLabel>
<RadioGroup aria-label={props.name} name={props.name} value={props.value}>
<FieldArray name="options">
{({ insert, remove, push }) => (
props.options.length > 0 && props.options.map((option,index) => (
<FormControlLabel key={index} {...props} value={option.toLowerCase()} control={<Radio />} label={option} />
))
)}
</FieldArray>
</RadioGroup>
</FormControl>
)
};
const CheckboxGroup = (props) => {
const [field, meta] = useField({...props, type: 'checkbox', });
return (
<FormControl component="fieldset">
<FormLabel component="legend">{props.label}</FormLabel>
<FormGroup>
<FieldArray name="options">
{({ insert, remove, push}) => (
props.options.length > 0 && props.options.map((option,index) => (
<FormControlLabel
{...field} {...props}
key={index}
control={<Checkbox />}
label={option.label}
/>
))
)}
</FieldArray>
</FormGroup>
<FormHelperText>Be careful</FormHelperText>
</FormControl>
)
}

I wrapped the whole <Form> in a function that passes props as an argument. I then get access to props.values.radio1. However, that has exposed that radio1 does not have a value even when it is clicked, which should be a separate issue.
{(props) => (
<Form>
<RadioInputGroup
label="Disable the checkbox?"
name="radio4"
options={['Yes','No']}
/>
<CheckboxGroup
disabled={props.values.radio1 === "No"}
name="checkbox1"
options={[
{name:"hello",label:"hello"},
{name:"there",label:"there"},
]}
/> </Form>
)}

Related

React: MUI autocomplete with MUI form

I've been using a MUI form like this:
<Box component="form" onSubmit={event => {
return handleSubmit(event);
}} noValidate sx={{mt: 1}}>
<TextField
margin="normal"
required
fullWidth
id="title"
label="Titel"
name="title"
autoFocus
/>
<TextField
margin="normal"
required
multiline
rows={10}
fullWidth
label="Inhalt"
name="content"
id="content"
autoComplete="off"
/>
</Box>
This allowed me to extract the values from the MUI TextField components by using FormData:
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
let newsResponse = await createNews({
title: data.get('title'),
content: data.get('content'),
});
}
This works fine. Now I wanted to add a MUI Autocomplete component to the form:
<Autocomplete
multiple
id="tags"
className={props.className}
open={open}
isOptionEqualToValue={(option, value) => option.name === value.name}
getOptionLabel={(option) => option.name}
options={tags}
renderInput={(params) => (
<TextField
{...params}
label="Tags"
required
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{loading ? <CircularProgress color="inherit" size={20}/> : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
)}
/>
However, I found no way to access the value of said Autocomplete component. It does not even have a name attribute and adding a name attribute to the inner TextField component does not work either.
How can I access the value of it in manner like data.get('tags')?
Considering that both are MUI components I would expect them to have the same API.
The useState hook, something like this:
function MyForm() {
const [values, setValues] = useState('');
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(values);
};
return (
<form onSubmit={handleSubmit}>
<Autocomplete
multiple
id="tags"
className={props.className}
open={open}
isOptionEqualToValue={(option, value) => option.name === value.name}
getOptionLabel={(option) => option.name}
options={tags}
onChange={(event: any, newValues: string[] | null) => {
setValues(newValues || '');
}}
renderInput={(params) => (
<TextField
{...params}
label="Tags"
required
InputProps={{
...params.InputProps,
endAdornment: (
<>
{loading ? <CircularProgress color="inherit" size={20}/> : null}
{params.InputProps.endAdornment}
</>
),
}}
/>
)}
/>
<button type="submit">Submit</button>
</form>
);
}

React Formik and Checkbox

I need to map dynamicaly on a array. This array can change with a search field and i need to know what checkbox are checked to send my data with a submit but i dont find the right way to do to check the checkbox with Formik. This is my code :
const initialValues = {
checked: [],
};
const onSubmit = (values) => {
console.log(values);
};
<Formik onSubmit={onSubmit} initialValues={initialValues}>
{({ values, handleSubmit }) => (
<form
onSubmit={handleSubmit}
noValidate={true}
>
{Listing.map((Search, index) => {
if (
Search.installationAddress.includes(
searchMeter
) ||
Search.deviceId.includes(searchMeter)
) {
return (
<div
key={index}
>
<Checkbox
color="text.standard"
label={`${Search.installationAddress}`+" Compteur N° " +`${Search.deviceId}`}
type="checkbox"
name="checked"
value={{
contractAccountId:
Search.contractAccountId,
deviceId: Search.deviceId,
}}
size="large"
/>
</div>
);
}
})}
<Checkbox
color="text.standard"
label="I confirm"
onChange={() => setValidationButton(!validationButton)}
value=""
checked={validationButton}
/>
<Button
type="submit"
disabled={!validationButton}
>
ACTIVATE
</Button>
</form>
)}
</Formik>

Material-UI TextField loses focus on every onChange

I am creating the following component:
It will contain an array of objects, where each object is a prescription, with the medicine name from the select and a TextField for the Dosis.
My problem is that the TextField loses focus on every onChange() and is very frustrating because it cannot be edited on a single focus.
This is my component :
const MedicineSelect = ({ medications, setMedications, ...props }) => {
const { medicines } = useMedicines()
const classes = useStyles()
const handleChange = (index, target) => {
// setAge(event.target.value)
const newMedications = cloneDeep(medications)
newMedications[index][target.name] = target.value
setMedications(newMedications)
}
const handleAddMedicine = () => {
const newMedications = cloneDeep(medications)
newMedications.push({ medicine: '', dosis: '', time: '' })
setMedications(newMedications)
}
const handleDeleteMedicine = (index) => {
console.log('DELETE: ', index)
const newMedications = cloneDeep(medications)
newMedications.splice(index, 1)
setMedications(newMedications)
}
return (
<Paper style={{ padding: 5 }}>
<List>
{medications.map((medication, index) => (
<ListItem key={nanoid()} divider alignItems='center'>
<ListItemIcon>
<Tooltip title='Eliminar'>
<IconButton
className={classes.iconButton}
onClick={() => handleDeleteMedicine(index)}
>
<HighlightOffOutlinedIcon />
</IconButton>
</Tooltip>
</ListItemIcon>
<FormControl className={classes.formControl}>
<InputLabel
id={`${index}-select-${medication}-label`}
>
Medicamento
</InputLabel>
<Select
labelId={`${index}-select-${medication}-label`}
id={`${index}-select-${medication}`}
name='medicine'
value={medication.medicine}
onChange={(event) =>
handleChange(index, event.target)
}
>
{medicines.map((medicine) => (
<MenuItem
key={nanoid()}
value={medicine.name}
>
{medicine.name}
</MenuItem>
))}
</Select>
</FormControl>
<TextField
// fullWidth
id={`${index}-text-${medication}`}
label='Dosis'
name='dosis'
onChange={(event) =>
handleChange(index, event.target)
}
value={medication.dosis}
/>
</ListItem>
))}
<Button onClick={handleAddMedicine}>+ agregar</Button>
</List>
</Paper>
)
}
And here is where I set the component:
const [medications, setMedications] = useState([
{ medicine: '', dosis: '', time: '' },
])
...
<Grid item md={12} xs={12}>
<Accordion>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls='panel1a-content'
id='panel1a-header'
>
<Typography variant='h4'>
Tratamiento:
</Typography>
</AccordionSummary>
<AccordionDetails>
<Container disableGutters>
<MedicineSelect
medications={medications}
setMedications={setMedications}
/>
</Container>
</AccordionDetails>
</Accordion>
</Grid>
...
Adding and removing objects from the array works perfect. selecting the medicine from the select, also works perfect. the only problem I have is when editing the Dosis TextField, with every character, the focus is lost and I have to click again on the TextField.
Please help me getting this fixed!!!
After searching a lot, finally I found the solution. Actually when using nanoid() to create unique keys, on every state update React re-renders all components and since the id of both the List and the TextField component are regenerated by nanoid on every render, React loses track of the original values, that is why Focus was lost.
What I did was keeping the keys unmuttable:
<ListItem key={`medication-${index}`} divider alignItems='center'>
and
<TextField
key={`dosis-${index}`}
fullWidth
// id={`${index}-dosis-${medication}`}
label='Dosis'
name='dosis'
onChange={(event) =>
handleChange(index, event.target)
}
value={medication.dosis}
/>

How to pass the value of input to component state on onChange method using formik

I have a helper for formik using it I manage my state on the form my problem is how can I change component state on onChange method I tried something like below but it doesnt work out !!! anyone can help me.. Whenever user trigger onChange method I want to take the value of it and request to api by this value and add new select on the form... Thank you so much
** custom formik lib
const MaterialUISelectField = ({
errorString,
label,
children,
value,
name,
onChange,
onBlur,
required,
}) => {
return (
<FormControl fullWidth>
<InputLabel required={required}>{label}</InputLabel>
<Select
name={name}
onChange={onChange}
onBlur={onBlur}
value={value}
>
{children}
</Select>
<FormHelperText error={true}> {errorString}</FormHelperText>
</FormControl>
);
};
const FormikSelect = ({ name, items, label, required = false, error }) => {
return (
<div className="FormikSelect">
<Field
name={name}
as={MaterialUISelectField}
label={label}
errorString={error ? error : <ErrorMessage name={name} />}
required={required}
>
<MenuItem disabled value="">
<em>{label} Seçiniz</em>
</MenuItem>
{items != null && items.map(item => (
<MenuItem key={item.Id} value={item.Id}>
{item.Ad}
</MenuItem>
))}
</Field>
</div>
);
};
** component where I use the custom formik lib
const [state, setState] = React.useState('');
<FormikSelect
name="aa"
label="aa"
items={data}
onChange={(e) => setState(e.target.value)} />

How can I pass a child component's state up to the parent?

I am new to ReactJS. Please forgive me if it is so simple.
I am trying to inject the radio button component (RadioButton.js) into home page. So that the radio button appear on home page. It like a child. As you can see from RadioButton.js, I have two radio buttons. Their values are buttonOne and buttonTwo.
What I am trying to achieve is that when buttonOne is selected, I would like to show <TablePage/> components. otherwise, <StickyHeadTable />
RadioButton.js
export default function FormControlLabelPosition() {
const [value, setValue] = React.useState("female");
const handleChange = event => {
setValue(event.target.value);
};
return (
<FormControl component="fieldset">
<RadioGroup
aria-label="position"
name="position"
value={value}
onChange={handleChange}
row
>
<FormControlLabel
value="buttonOne"
control={<Radio color="primary" />}
label="F1"
/>
<FormControlLabel
value="buttonTwo"
control={<Radio color="primary" />}
label="F2"
/>
</RadioGroup>
</FormControl>
);
}
RadioButton is injected in homepage. How can i get the values from RadioButton.js. So that I can use the condition.
HomePage.js
return (
<div className="home-page">
<RadioButton values={values} handleChange={handleChange}></RadioButton>
{values.flight === "buttonOne" ? <TablePage /> : <StickyHeadTable />}
</div>
);
RadioButton.js
export default function FormControlLabelPosition(props) {
return (
<FormControl component="fieldset">
<RadioGroup
aria-label="position"
name="position"
value={props.value}
onChange={props.handleChange}
row
>
<FormControlLabel
value="buttonOne"
control={<Radio color="primary" />}
label="F1"
/>
<FormControlLabel
value="buttonTwo"
control={<Radio color="primary" />}
label="F2"
/>
</RadioGroup>
</FormControl>
);
}
HomePage.js
const [value, setValue] = React.useState("female");
const handleChange = event => {
setValue(event.target.value);
};
return (
<div className="home-page">
<RadioButton values={values} handleChange={handleChange}></RadioButton>
{values.flight === "buttonOne" ? <TablePage /> : <StickyHeadTable />}
</div>
);
If you want to use the value from the RadioButton component, you should create it as an uncontrolled form component meaning that its value would come from it's parent, in this case the HomePage component.
So the RadioButton.js would be:
export default function RadioButton({ value, onChange }) {
return (
<FormControl component="fieldset">
<RadioGroup
aria-label="position"
name="position"
value={value}
onChange={onChange}
row
>
<FormControlLabel
value="buttonOne"
control={<Radio color="primary" />}
label="F1"
/>
<FormControlLabel
value="buttonTwo"
control={<Radio color="primary" />}
label="F2"
/>
</RadioGroup>
</FormControl>
);
}
RadioButton.propTypes = {
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired
};
And the HomePage.js
export default function HomePage() {
const [value, setValue] = React.useState("buttonOne");
const handleChange = event => {
setValue(event.target.value);
};
return (
<div className="home-page">
<RadioButton value={value} onChange={handleChange}></RadioButton>
{value === "buttonOne" ? <TablePage /> : <StickyHeadTable />}
</div>
);
}
On the HomePage.js you can use state for showing up the table conditionally based on radio button value.
I assume RadioButton.js component is called in HomePage.js as component.
RadioButton.js
export default function FormControlLabelPosition(props) {
const [value, setValue] = React.useState("female");
const handleChange = event => {
setValue(event.target.value);
> //Send your radio button value to parent component i.e HomePage.js
props.handleChange(event.target.value);
};
return (
<FormControl component="fieldset">
<RadioGroup
aria-label="position"
name="position"
value={value}
onChange={handleChange}
row
>
<FormControlLabel
value="buttonOne"
control={<Radio color="primary" />}
label="F1"
/>
<FormControlLabel
value="buttonTwo"
control={<Radio color="primary" />}
label="F2"
/>
</RadioGroup>
</FormControl>
);
}
HomePage.js
state = {
radioButtonValue: '';
}
render () {
return (
<div className="home-page">
<RadioButton handleChange={this.handleChange} />
{this.state.radioButtonValue === "buttonOne" ?
<TablePage /> : <StickyHeadTable />}
</div>
);
}
handleChange = (radioButtonValue) => {
this.setState({radioButtonValue});
}
One the above code, we are sending handleChange as a props and change the state as soon as radio-button is clicked and then rendering the table based on the state.

Categories

Resources