Change DropDown values on select of input using Formik - reactjs

Below is my form. What i am trying to do is on select of Customer i want to change the dropdown values of resolutionTypes.
<Formik
initialValues={formData}
onSubmit={(values, { setSubmitting }) => {
const fData = JSON.stringify(values, null, 2);
setSubmitting(true);
saveFilterFromData(JSON.parse(fData));
setTimeout(() => {
setSliderValue(value);
fetchOppReportData();
setSubmitting(false);
handleFilterVisible(false);
}, 1000);
}}
>
{({ submitForm, isSubmitting }) => (
<Form>
{isSubmitting && (
<ColoredLinearProgress
colorPrimary={colors.amber[200]}
barColorPrimary={colors.amber[800]}
/>
)}
<FormControl className={classes.formControl}>
<Field
component={TextField}
type="text"
name="selectedCustomer"
label="Select Customer"
select
variant="standard"
margin="normal"
InputLabelProps={{
shrink: true,
}}
>
{customerList.map(option => (
<MenuItem key={uuidv4()} value={option.label}>
{option.label}
</MenuItem>
))}
</Field>
</FormControl>
<FormControl className={classes.formControl}>
<Field
component={TextField}
type="text"
name="resolutionTypes"
label="Resolution Types"
select
variant="standard"
margin="normal"
InputLabelProps={{
shrink: true,
}}
>
{resolutionTypes.map(option => (
<MenuItem key={uuidv4()} value={option.value}>
{option.label}
</MenuItem>
))}
</Field>
<CaptureFormValueContext />
</FormControl>
</Formik>
Below is my CaptureFormValueContext function where I am trying to set resolution type values but I am facing an error: ' Maximum update depth exceeded error -'
Can anyone please tell me where I went wrong
const CaptureFormValueContext = () => {
// Grab values from context
const { values, submitForm, setFieldValue } = useFormikContext();
useEffect(() => {
if (values.selectedDateFilterType === CUSTOM) {
setIsCustom(true);
} else setIsCustom(false);
const customerId = Lo.filter(customerList, {
label: values.selectedCustomer,
});
const resolutionType = Lo.filter(customerResolutionData, {
CustomerId: customerId[0].value,
});
const mapResolutionType = MapResolutionTypes(resolutionType);
setFieldValue('resolutionTypes', mapResolutionType[0].value);
setResolutionTypes(mapResolutionType);
}, [values, submitForm, setFieldValue]);
return null;
};

Related

RHF not resetting values in an MUI checkbox group

I am trying to reset my form with the reset() method and, it is working partially. All of the form's fields get reset, except for the checkboxes group.
I have tried three different ways of implementing an MUI CheckboxesGroup but, I always get the same result.
You can see a working example in this sandbox
Checkbox one implementation
<FormControl error={error} required={required} component="fieldset">
<FormLabel component="legend">{label}</FormLabel>
<Controller
name={name}
render={({ field: { onBlur: rhfonBlur, onChange: rhfonChange } }) => (
<FormGroup onBlur={rhfonBlur}>
{Object.keys(options).map((key) => {
return (
<FormControlLabel
label={key}
key={key}
control={
<Checkbox onChange={(e) => rhfonChange(handleCheck(key))} />
}
/>
);
})}
</FormGroup>
)}
control={control}
/>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
Checkbox two implementation
<FormControl error={error}>
<FormLabel component="legend">{label}</FormLabel>
<FormGroup>
{Object.keys(options).map((key) => {
return (
<FormControlLabel
key={key}
control={<Checkbox {...register(name)} />}
value={key}
label={key}
/>
);
})}
</FormGroup>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
Checkbox three implementation
<FormControl required error={error} component="fieldset" variant="standard">
<FormLabel component="legend">{label}</FormLabel>
<Controller
control={control}
name={name}
render={({ field: { onChange: rfhonChange, value } }) => {
// console.log("value:", value);
return (
<FormGroup>
{Object.keys(state).map((key) => {
return (
<FormControlLabel
label={key}
key={key}
control={
<Checkbox
onChange={(e) => {
rfhonChange(handleChange(e));
}}
checked={state[key]}
name={key}
/>
}
/>
);
})}
</FormGroup>
);
}}
/>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
Your third implementation was the correct way of doing it by using RHF's <Controller /> component. The reason why it wasn't working was because of the following:
you're not updating RHF's internal state for your field as you are only passing a function to the onChange handler. There is no need to use an additional useState here
your initial value is a boolean (false) but you are using an object inside the <CheckboxThree /> component
So basically you can simplify the component to the following (in you're example it isn't clear if the field value should be an object or an array containing the selected options - so the example i made is using an array):
const CheckboxThree = ({
control,
error,
helperText,
label,
name,
options
}) => {
return (
<FormControl required error={error} component="fieldset" variant="standard">
<FormLabel component="legend">{label}</FormLabel>
<Controller
control={control}
name={name}
render={({ field: { onChange, value, ref, ...field } }) => (
<FormGroup>
{Object.keys(options).map((key) => {
return (
<FormControlLabel
label={key}
key={key}
control={
<Checkbox
{...field}
name={key}
checked={value.some((option) => option === key)}
onChange={(event, checked) => {
if (checked) {
onChange([...value, event.target.name]);
} else {
onChange(
value.filter((value) => value !== event.target.name)
);
}
}}
inputRef={ref}
/>
}
/>
);
})}
</FormGroup>
)}
/>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
);
};

Passing a function and parameters with onclick in react

i am trying to create a dynamic form where a user can enter the details of a guest list. I also want to be on every row a button called "email" where the user can click on it and an automatically generated email would be sent to the guest on that row. i have created the dynamic form and i have a fun a function that sends emails using (mailjs) what i cant do is pass the details of that particular row to the onlcick function so the email is sent to the right guest
my form code :
const useStyles = makeStyles((theme)=>({
root:{
"& .MuiTextField-root":{
margin: theme.spacing(1),
}
},
button:{
margin: theme.spacing(1)
}
}))
function Guests () {
const classes=useStyles()
const [inputFields, setInputField] = useState([
{firstName: "", lastName:"", email:"", rsvp:"", menue:""},
])
const handleSubmit =(e) =>{
e.preventDefault();
console.log("input fields", inputFields)
}
const handleChangeInput = (index, event) =>{
console.log(index, event.target.name)
const values =[...inputFields];
values[index][event.target.name] = event.target.value
setInputField(values)
}
const handleAddField = () =>{
setInputField([...inputFields,{firstName: "", lastName:"", email:"", rsvp:"", menue:""}])
}
const handleRemoveField =(index) =>{
const values = [...inputFields]
values.splice(index, 1)
setInputField(values)
}
return (
<Container>
<h1> Your Guest List</h1>
<form className={classes.root} onSubmit={handleSubmit}>
{inputFields.map((inputField, index)=>(
<div key={index}>
<TextField
name="firstName"
label="First Name"
value={inputField.firstName}
onChange={event => handleChangeInput(index, event)}
/>
<TextField
name="lastName"
label="Last Name"
value={inputField.lastName}
onChange={event => handleChangeInput(index, event)}
/>
<TextField
name="email"
label="Email"
value={inputField.email}
onChange={event => handleChangeInput(index, event)}
/>
<TextField
name="rsvp"
label="RSVP"
value={inputField.rsvp}
onChange={event => handleChangeInput(index, event)}
/>
<TextField
name="menue"
label="Menu Choice"
value={inputField.menue}
onChange={event => handleChangeInput(index, event)}
/>
<Button className={classes.button}
vareient="contained"
color="secondary"
>Send email</Button>
<IconButton
onClick={()=> handleRemoveField(index)}>
<RemoveIcon />
</IconButton>
<IconButton
onClick={() => handleAddField()}>
<AddIcon />
</IconButton>
</div>
))}
<Button
className={classes.button}
vareient="contained"
color="secondary"
type="submit"
onClick={handleSubmit}
endIcon={<Icon>save</Icon>}>
Save Guest List
</Button>
</form>
</Container>
)
}
export default Guests
my email function is :
function sendEmail (event){
event.preventDefault();
emailjs.sendForm(service,template,event.target,hero).then(res=>{
console.log(res)
alert("email sent thank you");
}).catch(err=> console.log(err));
}
Many thanks for you help

React hook form popular form with Material UI

I am trying to use react hook form with Material UI. At first, I want to populate the fields(textboxes, selects, autocompletes) after I fetch the user profile. How can I do that?
const [profileData, setProfileData] = useState(null);
const { control, handleSubmit, register, reset } = useForm();
useEffect(() => {
const getProfileData = async () => {
try {
const data = await api.get(profiles.getById, { id: profileId });
setProfileData(data);
} catch (err) {
console.log(`error getting: ${err}`);
}
};
getProfileData();
}, [profileId]);
<form onSubmit={handleSubmit(onSubmit)}>
<Flexbox flexDirection="column">
<Flexbox className="form-item">
<Select name="title" value={currentProfile.email}>
{dummyData.namePrefixes.map(index => (
<MenuItem key={index}>{index}</MenuItem>
))}
</Select>
<TextField
inputRef={register}
name="Name"
label="* Name"
InputLabelProps={{
shrink: true
}}
variant="outlined"
placeholder="Name"
className="form-item-full"
/>
</Flexbox>
<TextField
inputRef={register}
label="* Hospital Name"
name="hospital"
className="form-item"
placeholder="Hospital"
InputLabelProps={{
shrink: true
}}
variant="outlined"
/>
<Autocomplete
className="form-item"
options={countries}
getOptionLabel={option => option.name}
renderInput={params => (
<TextField
{...params}
label="Country"
placeholder="Select a Country"
InputLabelProps={{
shrink: true
}}
variant="outlined"
/>
)}
/>
const [profileData, setProfileData] = useState(null);
const { control, handleSubmit, register, reset } = useForm();
useEffect(() => {
getProfileData();
}, []);
const getProfileData = async () => {
try {
const data = await api.get(profiles.getById, { id: profileId });
setProfileData(data);
} catch (err) {
console.log(`error getting: ${err}`);
setProfileData(null);
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<TextField
inputRef={register}
name="Name"
label="* Name"
InputLabelProps={{
shrink: true
}}
value={profileData?.name}
variant="outlined"
placeholder="Name"
className="form-item-full"
/>
<TextField
inputRef={register}
label="* Hospital Name"
name="hospital"
className="form-item"
placeholder="Hospital"
InputLabelProps={{
shrink: true
}}
value={profileData?.hospitalName}
variant="outlined"
/>
</form>
)

Formik Select label as Placeholder

Help me to solve , need to be label as place holder in formik react js
the code as follows
import * as React from 'react';
import {render} from 'react-dom';
import {Formik, Field, Form} from 'formik';
import {
Button,
LinearProgress,
MenuItem,
FormControl,
InputLabel,
FormControlLabel,
} from '#material-ui/core';
import MuiTextField from '#material-ui/core/TextField';
import {
fieldToTextField,
TextField,
TextFieldProps,
Select,
Switch,
} from 'formik-material-ui';
interface Values {
email: string;
}
const ranges = [
{
value: 'none',
label: 'None',
},
{
value: '0-20',
label: '0 to 20',
},
{
value: '21-50',
label: '21 to 50',
},
{
value: '51-100',
label: '51 to 100',
},
];
const UppercasingTextField = (props: TextFieldProps) => (
<MuiTextField
{...fieldToTextField(props)}
onChange={event => {
const {value} = event.target;
props.form.setFieldValue(
props.field.name,
value ? value.toUpperCase() : ''
);
}}
/>
);
const App = () => (
<Formik
initialValues={{
email: '',
password: '',
select: '',
tags: [],
rememberMe: true,
}}
validate={values => {
const errors: Partial<Values> = {};
if (!values.email) {
errors.email = 'Required';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)
) {
errors.email = 'Invalid email address';
}
return errors;
}}
onSubmit={(values, {setSubmitting}) => {
setTimeout(() => {
setSubmitting(false);
alert(JSON.stringify(values, null, 2));
}, 500);
}}
render={({submitForm, isSubmitting, values, setFieldValue}) => (
<Form>
<Field
name="email"
type="email"
label="Email"
variant="outlined"
component={UppercasingTextField}
/>
<br />
<Field
type="password"
label="Password"
name="password"
component={TextField}
/>
<br />
<FormControlLabel
control={
<Field label="Remember Me" name="rememberMe" component={Switch} />
}
label="Remember Me"
/>
<br />
<Field
type="text"
name="select"
label="With Select"
select
variant="outlined"
helperText="Please select Range"
margin="normal"
component={TextField}
InputLabelProps={{
shrink: true,
}}
>
{ranges.map(option => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Field>
<br />
<FormControl>
<InputLabel shrink={true} htmlFor="tags">
Tags
</InputLabel>
<Field
type="text"
name="tags"
component={Select}
variant="outlined"
multiple={true}
inputProps={{name: 'tags', id: 'tags'}}
>
<MenuItem value="dogs">Dogs</MenuItem>
<MenuItem value="cats">Cats</MenuItem>
<MenuItem value="rats">Rats</MenuItem>
<MenuItem value="snakes">Snakes</MenuItem>
</Field>
</FormControl>
<br />
{isSubmitting && <LinearProgress />}
<br />
<Button
variant="contained"
color="primary"
disabled={isSubmitting}
onClick={submitForm}
>
Submit
</Button>
</Form>
)}
/>
);
render(<App />, document.getElementById('root'));
To show the With Select label as place holder at first time. While we select an option the with select label shows on top . In this code the label always shows at top.
the example here.Thanks in advance
just remove shrink={true}
<FormControl>
<InputLabel htmlFor="tags">
Tags
</InputLabel>
<Field
type="text"
name="tags"
component={Select}
variant="outlined"
multiple={true}
inputProps={{name: 'tags', id: 'tags'}}
>
<MenuItem value="dogs">Dogs</MenuItem>
<MenuItem value="cats">Cats</MenuItem>
<MenuItem value="rats">Rats</MenuItem>
<MenuItem value="snakes">Snakes</MenuItem>
</Field>
</FormControl>

Can't add items to material-ui form

I got handed down a project from someone else and I need to add some items to a form created with react and material-ui. I can get text fields to work but if I try to add a dropdown, it doesn't hold the value when selected.
I followed the same conventions of adding the items to the form that the guy who created this form used.
I added a grid item with a formcontrol component inside of it. There was also defaultprops set with a "game" object inside, that had all of the form fields as properties. I added the new field items to there also.
Here is the whole form component because I'm not really sure where the problem actually is.
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import history from '~/utils/history';
import { FormControl, Button, TextField, Grid, MenuItem, FormHelperText } from '#material-ui/core';
import { DateTimePicker } from 'material-ui-pickers';
import gameState from '~/utils/enums';
import { dateTime } from '~/utils/formats';
import { getById, create, update, remove } from '~/services/games';
import { getAll as getLeagues } from '~/services/leagues';
import { getAll as getSeasons } from '~/services/seasons';
import { getAll as getTeams } from '~/services/teams';
const GameForm = ({ game, id }) => {
useEffect(() => {
console.log({ form, game });
});
const [edit, setEdit] = useState(false);
const [form, setForm] = useState(game);
const [error, setError] = useState('');
const [variant, setVariant] = useState('outlined');
const [leagues, setLeagues] = useState([]);
const [seasons, setSeasons] = useState([]);
const [teams, setTeams] = useState([]);
const [gametype, setGametype] = useState(1);
const gametypes = [
{ key: 0, value: 'Series Game' },
{ key: 1, value: 'Playoff' },
{ key: 2, value: 'Final' },
{ key: 3, value: 'Else' }
];
useEffect(() => {
getSeasons()
.then(({ data }) => {
setSeasons(data);
return getLeagues();
})
.then(({ data }) => {
setLeagues(data);
if (id) {
getById(id).then(({ data }) => setForm(data));
} else {
setEdit(true);
}
});
}, []);
useEffect(() => {
if (edit) {
setVariant('outlined');
} else {
setVariant('standard');
}
}, [edit]);
useEffect(() => {
if (form.league) {
getTeams({ leagues: [form.league] }).then(({ data }) => setTeams(data));
}
}, [form.league]);
useEffect(() => {
if (form.gametype) {
setGametype('Playoff');
}
}, [form.gametype]);
const handleChange = ({ target }) => {
let { name, value } = target;
setForm({
...form,
[name]: value
});
};
const handleDateChange = formInputName => {
const dateHandler = moment => {
setForm({
...form,
[formInputName]: moment ? moment.toDate() : null
});
};
return dateHandler;
};
const handleFormSubmit = () => {
if (!edit) {
setEdit(true);
} else {
new Promise(resolve => {
resolve(form._id ? update(form) : create(form));
})
.then(() => {
history.push('/games');
})
.catch(error => setError(error.response.data.error));
}
};
const handleDelete = () => {
if (window.confirm(`Delete permanently?`)) {
remove(id).then(() => history.goBack());
}
};
const handleCancel = () => {
if (edit && id) {
setEdit(false);
} else {
history.goBack();
}
};
return (
<Grid container spacing={8} justify="space-between">
<Grid item xs={12} md={6}>
<FormControl margin="normal" required fullWidth variant="outlined">
<TextField
required
select
id="season"
name="season"
label="Season"
variant={variant}
disabled={!edit}
value={form.season}
onChange={handleChange}
>
{seasons.map(option => (
<MenuItem key={option.name} value={option._id}>
{option.name}
</MenuItem>
))}
</TextField>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl margin="normal" required fullWidth variant="outlined">
<TextField
required
select
id="league"
name="league"
label="League"
variant={variant}
disabled={!edit}
value={form.league}
onChange={handleChange}
>
{leagues.map(option => (
<MenuItem key={option.name} value={option._id}>
{option.name}
</MenuItem>
))}
</TextField>
</FormControl>
</Grid>
<Grid item xs={12}>
<FormControl margin="normal" required fullWidth variant="outlined">
<DateTimePicker
required
id="start"
name="start"
label="Game starts"
variant={variant}
disabled={!edit || !form.season}
autoOk
ampm={false}
keyboard
clearable
minDate={form.season ? seasons.find(({ _id }) => _id === form.season).start : undefined}
minDateMessage="Cannot start before the season begins"
maxDate={form.season ? seasons.find(({ _id }) => _id === form.season).end : undefined}
maxDateMessage="Cannot start after the end of the season"
value={form.start}
onChange={handleDateChange('start')}
{...dateTime}
/>
<FormHelperText disabled={!form.season}>
{!form.season ? 'Select season first' : undefined}
</FormHelperText>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl margin="normal" required fullWidth variant="outlined">
<TextField
required
select
id="home"
name="home"
label="Home team"
variant={variant}
disabled={!edit || !form.league}
helperText={!form.league ? 'Select league first' : undefined}
value={form.home}
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
{teams
.filter(({ _id }) => _id !== form.away)
.map(option => (
<MenuItem key={option.name} value={option._id}>
{option.name}
</MenuItem>
))}
</TextField>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl margin="normal" required fullWidth variant="outlined">
<TextField
required
select
id="away"
name="away"
label="Team away"
variant={variant}
disabled={!edit || !form.league}
helperText={!form.league ? 'Select league first' : undefined}
value={form.away}
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
{teams
.filter(({ _id }) => _id !== form.home)
.map(option => (
<MenuItem key={option.name} value={option._id}>
{option.name}
</MenuItem>
))}
</TextField>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl margin="normal" required fullWidth variant="outlined">
<TextField
required
select
id="gametype"
label=" Game type"
variant={variant}
disabled={!edit}
value={form.gametype}
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
{gametypes.map(option => (
<MenuItem key={option.key} value={option.value}>
{option.value}
</MenuItem>
))}
</TextField>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl margin="normal" required fullWidth variant="outlined">
<TextField
text
id="umpire"
label="Umpire"
variant={variant}
disabled={!edit || !form.league}
/>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl margin="normal" required fullWidth variant="outlined">
<TextField
text
id="scorekeeper"
label="Scorekeeper"
variant={variant}
disabled={!edit || !form.league}
/>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl margin="normal" required fullWidth variant="outlined">
<TextField
text
id="referee1"
label="Referee 1"
variant={variant}
disabled={!edit || !form.league}
/>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl margin="normal" required fullWidth variant="outlined">
<TextField
text
id="referee2"
label="Referee 2"
variant={variant}
disabled={!edit || !form.league}
/>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl margin="normal" required fullWidth variant="outlined">
<TextField
text
id="referee3"
label="Referee 3"
variant={variant}
disabled={!edit || !form.league}
/>
</FormControl>
</Grid>
<Grid item xs={edit && id ? 4 : 6}>
<Button variant="outlined" color="secondary" fullWidth onClick={handleCancel}>
{edit && id ? 'Cancel' : 'Back'}
</Button>
</Grid>
{edit && id ? (
<Grid item xs={4}>
<Button
type="submit"
variant="contained"
color="secondary"
fullWidth
onClick={handleDelete}
>
Delete
</Button>
</Grid>
) : null}
<Grid item xs={edit && id ? 4 : 6}>
<Button
type="submit"
variant="outlined"
color="primary"
fullWidth
onClick={handleFormSubmit}
>
{edit ? (id ? 'Save' : 'Create') : 'Edit Game'}
</Button>
</Grid>
{!edit ? (
<Grid item xs={12}>
<Button
variant="outlined"
color="primary"
fullWidth
onClick={() => {
history.push('/games/' + id + '/scores');
}}
>
EDIT SCORES
</Button>
</Grid>
) : null}
</Grid>
);
};
GameForm.defaultProps = {
game: {
season: '',
league: '',
start: null,
home: '',
away: '',
gametype: '',
umpire: '',
scorekeeper: '',
referee1: '',
referee2: '',
referee3: ''
}
};
GameForm.propTypes = {
//classes: PropTypes.object.isRequired,
game: PropTypes.object
};
export default GameForm;
I noticed that if I print the game object to the console, it only shows the old form items and not the new ones I added. The new items are: gametype, umpire, scorekeeper, referee1-3.
I've been trying to make this work for a few days already, so any help would really be appreciated.
you need onChange={handleChange} in those and you also need to provide the name as props
BTW, I highly recommend you to combine those useStates as one single state object
For example, an alternative banned multiple useState() calls in a component. You’d keep state in one object.
function Form() {
const [state, setState] = useState({
edit: '',
form: '',
error: '',
});
// ...
}
To be clear, Hooks do allow this style. You don’t have to split your state into a bunch of state variables (see doc).

Resources