React hook form popular form with Material UI - reactjs

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>
)

Related

mui autocomplete with react-hook-form: defaultValue in formData

I'm setting the defaultValue of the Autocopmlete component as below:
<Controller
control={control}
name={name}
render={({field: {onChange, value}}) => (
<Autocomplete
freeSolo={freeSolo}
options={options}
renderInput={params => {
return <TextField {...params} label={label} margin="normal" variant="outlined" onChange={onChange} />
}}
onChange={(event, values, reason) => onChange(values)}
defaultValue={defaultValue}
/>
)}
/>
the value is well displayed based on the defaultValue.
However when I click on the submit button, the value of the Autocomplete field is always undefined if I don't use the autocomplete component.
Here are how I register the hook and component (simplified code)
const customerSchema = yup.object().shape({
route: yup.string().nullable()
})
const {control, handleSubmit} = useForm({
resolver: yupResolver(customerSchema)
})
const onSubmit = formData => {
console.log(formData) // autocomplete is undefined if no action on the autocomplete
}
<form noValidate autoComplete="off" onSubmit={handleSubmit(onSubmit)}>
<AutoCompleteInput
control={control}
name="route"
label="route"
options={customers_routes.map(option => option.route_name)}
defaultValue={customer.route}
freeSolo={false}
/>
<Button type="submit">
Update
</Button>
</form>
What should I do to have the route field always available in the form data when I click on submit?
Why don't you use defaultValues option with useForm:
const {control, handleSubmit} = useForm({
resolver: yupResolver(customerSchema),
defaultValues: { route: customer.route },
});
and instead of sending defaultValue prop to AutoComplete you can use value prop, like this:
<Controller
control={control}
name={name}
render={({ field: { onChange, value } }) => (
<Autocomplete
freeSolo={freeSolo}
options={options}
renderInput={(params) => {
return (
<TextField
{...params}
label={label}
margin="normal"
variant="outlined"
onChange={onChange}
/>
);
}}
onChange={(event, values, reason) => onChange(values)}
value={value}
/>
)}
/>
Here is a simplest way to use an Autocomplete with react hook from , render your Autocomplete component inside Controller from react hook from, use onChange and value from the render function to control the value
<Controller
control={control}
name="type"
rules={{
required: 'Veuillez choisir une réponse',
}}
render={({ field: { onChange, value } }) => (
<Autocomplete
freeSolo
options={['field', 'select', 'multiple', 'date']}
onChange={(event, values) => onChange(values)}
value={value}
renderInput={(params) => (
<TextField
{...params}
label="type"
variant="outlined"
onChange={onChange}
/>
)}
/>
)}

Change DropDown values on select of input using Formik

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;
};

Unhandled Rejection (Error): expecting a value for non-nullable variable: stime of type: time! in variableValues

I am getting this error in the code.Can u please help me this. This is my code.
My mutation
const ADD_SCHEDULE = gql`
mutation MyMutation($date:date!,$etime:time! ,$stime:time!,$title:String!) {
MyMutation(date: $date, etime: $etime, stime: $stime, title: $title) {
affected_rows
}
}
`;
Create function for creating a schedule.
const Create = () =>
let date,title,stime,etime;
const[createschedule] = useMutation(ADD_SCHEDULE);
return (
<div>
<form
onSubmit = {(e)=>{
e.preventDefault();
createschedule({variables:{title:title.value, date:date.value ,stime:stime.value,etime:etime.value}});
}}>
<TextField id="title" ref={value => title=value} label="Title" />
<TextField
id="date"
defaultValue="2017-05-24"
label="Date"
type="date"
ref={value => date=value}
InputLabelProps={{
shrink: true,
}}
/>
<TextField
id="stime"
label="STime"
ref={value => stime=value}
type="time"
defaultValue="07:30"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
/>
<TextField
id="etime"
label="ETitle"
type="time"
ref={value => etime=value}
defaultValue="07:30"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300,
}}
/>
<button type="submit">Create</button>
</form>
</div>
);
}
export default Create
Separate problems
test API using graphiql/playground (and of course query variables) to check required arg/variables format (for custom date/type types/scalars)
... then check if your client (react app) sends variables in required format ... explore browsers dev tools network request body - compare it detaily with playground

Validations not triggered during on click on submit button

I would like to display required, min 1 and max 5 validations in following fields in material-ui dialog. At the moment I have added for one field, but some how validation is not displaying during on click on submit button in dialog. Could someone please advise me on how can i fix the problem.
demo link added here:
https://codesandbox.io/s/tender-wood-3jrwu?file=/src/App.js
const App = () => {
const [rating, setRating] = useState({
ballshooting: "",
dribbling: "",
ballcontrol: "",
sprinting: "",
fitness: ""
});
const [ratingTotal, setRatingTotal] = useState(0);
const [open, setOpen] = React.useState(false);
const { handleSubmit, register, errors } = useForm();
const isMounted = useRef(false);
useEffect(() => {
isMounted.current = true;
return () => (isMounted.current = false);
}, []);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const onChange = e => {
e.persist();
const ratingValues = {
...rating,
[e.target.name]: e.target.value
};
setRating(ratingValues);
ratingCalculation(ratingValues);
};
const ratingCalculation = ratingValues => {
const {
ballshooting,
dribbling,
ballcontrol,
sprinting,
fitness
} = ratingValues;
const newTotal =
Number(ballshooting) +
Number(dribbling) +
Number(ballcontrol) +
Number(sprinting) +
Number(fitness);
const finalAvg = newTotal / 5;
setRatingTotal(finalAvg);
return ratingTotal;
};
return (
<form className="ratingForm">
<Button
className="playerRatingBtn"
onClick={() => handleClickOpen()}
variant="outlined"
color="primary"
>
Enter Player Rating
</Button>
<Dialog open={open} onClose={handleClose} aria-labelledby="skills-area">
<DialogTitle id="skills-area">Rate the skills of a player</DialogTitle>
<DialogContent>
<label>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="ballshooting"
label="Ball Shooting"
type="text"
fullWidth
min={1}
max={5}
ref={register({
required: "Soccer ball shooting skill is required !"
})}
/>
<span className="newsErrorTextFormat">
{errors.shooting && errors.shooting.message}
</span>
</label>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="dribbling"
label="Dribbling"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="ballcontrol"
label="Ball Control"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="sprinting"
label="Sprinting"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="fitness"
label="Fitness"
type="text"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="secondary">
Cancel
</Button>
<Button type="submit" onClick={() =>
handleClose(
handleSubmit(calculateAvgRating(ratingCalculation(rating)))
)
} color="primary">
Submit
</Button>
</DialogActions>
</Dialog>
</form>
);
};
export default App;

How can I pass email displaying in DialogContentText to axios put request

How can I pass ratePlyrEmail to calculateAvgRating() during on click on submit button.
The player email will displays randomly in the <Input type="hidden" name="playertorate" value={email} onChange={onChange}></Input> of DialogContentText every time when the user click on Enter Player Rating button. I have added onChange in the DialogContentText. Now I am getting only rating and loginUserEmail in server side.
const [ratePlyrEmail, setRatePlyrEmail] = useState({ playertorate: ''});
const [rating, setRating] = useState({
shooting: "",
dribbling: "",
ballcontrol: "",
sprinting: "",
fitness: ""
});
const [ratingTotal, setRatingTotal] = useState(0);
const onChange = e => {
e.persist();
const ratingValues = {
...rating,
[e.target.name]: e.target.value
};
setRating(ratingValues);
const rateEmailValue = {
...ratePlyrEmail,
[e.target.name]: e.target.value
}
setRatePlyrEmail(rateEmailValue);
ratingCalculation(ratingValues);
};
const ratingCalculation = ratingValues =>{
const {
shooting,
dribbling,
ballcontrol,
sprinting,
fitness
} = ratingValues;
const newTotal =
Number(shooting) +
Number(dribbling) +
Number(ballcontrol) +
Number(sprinting) +
Number(fitness);
const finalAvg = newTotal / 5;
setRatingTotal(finalAvg);
return ratingTotal ;
}
const calculateAvgRating = (rating) => {
const sendPlayerRating = async () => {
try {
const params = {
email: loginUserEmail,
};
const res = await axios.put('http://localhost:8000/service/playerrating', {ratingTotal:rating}, {params} );
if (res.data.success) {
//rest of code to continue here...
}
else {
console.log(res.data.message);
}
} catch (e) {
console.log(e.response.data.message);
}
}
sendPlayerRating();
};
<div className="container">
<div className="weeklycount_graph_section">
<div>
<Button className="playerRatingBtn" variant="outlined" color="primary" onClick={() => handleClickOpen(setRandPlayerRating())}>
Enter Player Rating
</Button>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="skills-area"
>
<DialogTitle id="skills-area">Rate the skills of a player</DialogTitle>
<DialogContent>
{
randPlayers.map(({email, name, photo}) =>(
<DialogContentText key="id">
<Avatar alt="player" src={photo}/>
<Input type="hidden" name="playertorate" value={email} onChange={onChange}></Input>
</DialogContentText>
))
}
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="shooting"
label="Shooting"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="dribbling"
label="Dribbling"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="ballcontrol"
label="Ball Control"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="sprinting"
label="Sprinting"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="fitness"
label="Fitness"
type="text"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="secondary">
Cancel
</Button>
<Button onClick={() => handleClose(calculateAvgRating((ratingCalculation(rating))))} color="primary">
Submit
</Button>
</DialogActions>
</Dialog>
</div>
</div>
</div>

Resources