I'm trying to implement a form with validation:
import React from 'react';
import { useForm } from "react-hook-form";
import axios, {AxiosResponse} from "axios";
import {Box, Button, Container, Grid, Typography} from "#material-ui/core";
import TextField from '#material-ui/core/TextField';
export async function postTicket(data: TicketDTO): Promise<AxiosResponse<TicketDTO[]>> {
return await axios.post<TicketDTO[]>(
`http://localhost:8080/api/support/tickets/create`
);
}
export interface TicketDTO {
title?: string;
}
export default function OpenTicket(props: any) {
const {
register,
handleSubmit,
formState: { errors }
} = useForm<TicketDTO>();
const onSubmit = async (data: TicketDTO) => {
try {
await postTicket(data);
} catch (err) {
console.log(err);
}
};
return (
<Container>
<form onSubmit={handleSubmit(onSubmit)}>
<TextField
id="outlined-full-width"
label="Name"
{...register("title", { required: true, maxLength: 20 })}
placeholder="Placeholder"
helperText="Full width!"
fullWidth
margin="normal"
InputLabelProps={{
shrink: true,
}}
variant="outlined"
/>
<Button
variant="contained"
color="primary"
size="small"
>
Submit
</Button>
</form>
</Container>
);
}
Sandbox: https://stackblitz.com/edit/react-ts-4pnf8b?file=Hello.tsx
But when I click submit button nothing happens. I would like to use the Materia-ui default validation messages? Do you know where I'm wrong?
I have made some modifications to your code,
Added type to your button as submit
required can be directly added to Text-Field.
maxLength can be applied as part of inputProps.
I am not quite sure what you are doing in your submit event. Ignoring for now.
https://stackblitz.com/edit/react-ts-dufrzd?file=Hello.tsx
<form onSubmit={handleSubmit(onSubmit)}>
<TextField
id="outlined-full-width"
label="Name"
required
placeholder="Placeholder"
helperText="Full width!"
fullWidth
margin="normal"
InputLabelProps={{
shrink: true,
}}
inputProps={{maxLength:20}}
variant="outlined"
/>
<Button variant="contained" color="primary" size="small" type="submit">
Submit
</Button>
</form>
Related
I am new to ReactJS with MUI development, have below ReactJS TypeScript with MuiText filed form. Looking some help to use useSate method to change the textfiled value.
Also add the onchnage function for the text filed. I can add the onchange function for normal text filed, unsure how to add it for MUI Text filed?
import * as React from 'react';
import { useState } from "react"
import Button from '#mui/material/Button';
import CssBaseline from '#mui/material/CssBaseline';
import TextField from '#mui/material/TextField';
import Grid from '#mui/material/Grid';
import Box from '#mui/material/Box';
import Container from '#mui/material/Container';
import { createTheme, ThemeProvider } from '#mui/material/styles';
import { useForm, SubmitHandler, Controller } from 'react-hook-form';
import * as yup from 'yup';
import { yupResolver } from '#hookform/resolvers/yup';
interface IFormInputs {
filepath: string;
}
const schema = yup.object().shape({
filepath: yup.string().min(4).required(),
});
const theme = createTheme();
export default function MuiTextField() {
const {
control,
handleSubmit,
formState: { errors },
} = useForm<IFormInputs>({
resolver: yupResolver(schema),
});
const [filepath, setFilepath] = useState("vodeo.mp3");
const onSubmit: SubmitHandler<IFormInputs> = (data) => {
console.log('data submitted: ', data);
console.log('filepath: ', data.filepath);
};
return (
<ThemeProvider theme={theme}>
<Container component="main" maxWidth="lg">
<CssBaseline />
<Box
sx={{
marginTop: 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<form onSubmit={handleSubmit(onSubmit)}>
<Box sx={{ mt: 3 }}>
<Grid container spacing={2}>
<Grid item xs={16} sm={6}>
<Controller
name="filepath"
control={control}
defaultValue=""
render={({ field }) => (
<TextField
{...field}
label="File Path"
error={!!errors.filepath}
helperText={errors.filepath ? errors.filepath?.message : ''}
autoComplete="file-path"
fullWidth
/>
)}
/>
</Grid>
<Button
type="submit"
variant="contained"
sx={{ mt: 3, mb: 2 }}
>
Submit
</Button>
</Grid>
</Box>
</form>
</Box>
</Container>
</ThemeProvider>
);
}
Update:
Here is the codeshare: https://codesandbox.io/s/boring-water-m47uxn?file=/src/App.tsx
When we change the text box value to auto, want to change the textbox value to audio.mp3. but its not working.
MUI Textfield have onChange too:
<TextField
error={Boolean(touched.name && errors.name)}
fullWidth
label={t('Name')}
name="name"
onBlur={handleBlur}
onChange={handleChange}
value={values.name}
variant="outlined"
autoComplete="off"
/>
'field' in render function contains onChange.
And state of form saved in useForm. In useForm props you have to add defaultValues. And you did not pass prop type="file", maybe its your problem.
Guide about create file input with react hook form: https://refine.dev/blog/how-to-multipart-file-upload-with-react-hook-form/
Textfield api: https://mui.com/material-ui/api/text-field/
import React, { useState } from "react";
import FileBase64 from "react-file-base64";
import { useDispatch } from "react-redux";
import { makeStyles } from "#material-ui/core/styles";
import { TextField, Select, Input, MenuItem, Button } from "#material-ui/core";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "#hookform/resolvers/yup";
import * as yup from "yup";
import { updatePost } from "../actions/post";
const useStyles = makeStyles((theme) => ({
textField: {
marginBottom: theme.spacing(2),
},
buttons: {
marginTop: theme.spacing(2),
},
}));
const tags = ["fun", "programming", "health", "science"];
const postSchema = yup.object().shape({
title: yup.string().required(),
subtitle: yup.string().required(),
content: yup.string().min(20).required(),
tag: yup.mixed().oneOf(tags),
});
const EditPostForm = ({ history, post, closeEditMode }) => {
const dispatch = useDispatch();
const [file, setFile] = useState(post?.image);
const { register, handleSubmit, control, errors, reset } = useForm({
resolver: yupResolver(postSchema),
});
const onSubmit = (data) => {
const updatedPost = {
_id: post._id,
...data,
image: file,
};
dispatch(updatePost(post._id, updatedPost));
reset();
setFile(null);
closeEditMode();
};
const classes = useStyles();
return (
<div>
<form noValidate autoComplete="off" onSubmit={handleSubmit(onSubmit)}>
<TextField
id="title"
label="Başlık"
name="title"
variant="outlined"
className={classes.textField}
size="small"
{...register('title')}
error={errors?.title ? true : false}
fullWidth
defaultValue={post?.title}
/>
<TextField
id="subtitle"
label="Alt Başlık"
name="subtitle"
variant="outlined"
className={classes.textField}
size="small"
{...register('subtitle')}
error={errors?.subtitle ? true : false}
fullWidth
defaultValue={post?.subtitle}
/>
<Controller
render={({field}) => (
<Select
{...field}
input={<Input />}
className={classes.textField}
fullWidth
>
{
tags.map((tag, index) => (
<MenuItem {...field} key={index} value={tag}>
{tag}
</MenuItem>
))
}
</Select>
)}
name='tag'
control={control}
error={errors?.tag ? true : false}
defaultValue={tags[0]}
/>
<TextField
id="content"
label="İçerik"
name="content"
multiline
size="small"
{...register('content')}
rows={16}
className={classes.textField}
variant="outlined"
error={errors?.content ? true : false}
fullWidth
defaultValue={post?.content}
/>
<FileBase64 multiple={false} onDone={({ base64 }) => setFile(base64)} />
<div className={classes.buttons}>
<Button color="primary" variant="outlined" onClick={closeEditMode}>
Vazgeç
</Button>{" "}
<Button color="secondary" variant="outlined" type="submit" >
Kaydet
</Button>
</div>
</form>
</div>
);
};
export default EditPostForm;
I have EditPostForm component, component doesn't give any error but when I tried to submit my form onSubmit function is not triggered.
I used react-hook-form to create my form and I used material UI components inside form.
When I Click button which has type submit does not trigger onSubmit function which is called inside of handleSubmit. Why onSubmit is not triggered?
onSubmit isn't triggered because you may have form errors
You can get errors from formState object (const { formState } = useForm(...))
And then use error={formState.errors?.content ? true : false} in your code
https://react-hook-form.com/api/useform/formstate
See an example here
https://codesandbox.io/s/keen-burnell-2yufj?file=/src/App.js
You need to pass onSubmit and onError both.
Like this:
onPress={handleSubmit(onSubmit, onErrors)}
I faced the same error, the problem was that, my register were Boolean and my input was string, and since the value was not required It didn't show errors until I figure out the problem and change register from Boolean to string
I am using react-hook-form V7 with Material UI to create a simple form however, there is a weird behavior when setting the mode in useForm({mode: ".."})
Here is my code snippet:
import { yupResolver } from "#hookform/resolvers/yup";
import { Button, Grid, Link, TextField, Typography } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
import Alert from "#material-ui/lab/Alert";
import { Controller, useForm } from "react-hook-form";
import { _MESSAGES } from "src/constants/messages";
import * as yup from "yup";
import AuthContainer from "./AuthContainer";
// ##################### Helpers ######################
/**
* #param {object} error
* #param {string} msg
*/
const renderError = (error, msg) =>
error ? <Alert severity="error">{msg}</Alert> : "";
// ############## Schema & Default Values ##############
const schema = yup.object().shape({
email: yup.string().required(),
password: yup.string().required(),
});
const defaultValues = {
email: "",
password: "",
};
// ###################### Styles ######################
const useStyles = makeStyles((theme) => ({
form: {
width: "100%", // Fix IE 11 issue.
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
}));
// ################# Main Component ###################
const SignInPage = () => {
const classes = useStyles();
const {
handleSubmit,
formState: { errors },
reset,
control,
} = useForm({
mode: "all",
resolver: yupResolver(schema),
defaultValues,
});
const onSubmit = (data) => {
console.log(data);
// Reset form fields
reset();
};
console.log(errors);
const Form = () => (
<>
<Typography component="h1" variant="h5">
Sign In
</Typography>
<form className={classes.form} onSubmit={handleSubmit(onSubmit)}>
<Controller
control={control}
name="email"
render={({ field }) => (
<TextField
variant="outlined"
margin="normal"
fullWidth
autoComplete="email"
label="Email Address"
{...field}
error={errors.email?.message ? true : false}
/>
)}
/>
{renderError(errors.email?.message, _MESSAGES.required)}
<Controller
control={control}
defaultValue=""
name="password"
render={({ field }) => (
<TextField
variant="outlined"
margin="normal"
fullWidth
autoComplete="current-password"
type="password"
label="Password"
{...field}
error={errors.password?.message ? true : false}
/>
)}
/>
{renderError(errors.password?.message, _MESSAGES.required)}
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
<Grid container>
<Grid item xs={12}>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
<Grid item>
<Link href="#" variant="body2" color="secondary">
{"Don't have an account? Sign Up"}
</Link>
</Grid>
</Grid>
</form>
</>
);
return <AuthContainer Form={<Form />} />;
};
export default SignInPage;
What might have gone wrong?
Here is a Gif of what happened:
selected the input field
started typing
after one char it leaves the input field so, I need to select it to type again.
Move your Form component to its own Component, otherwise, you are mount and unmount your Form Component each re-render.
const App = () => {
const Form = () => {} // mount and unmount with each re-render
return <div><Form /></div>
}
to
const Form = () => {}
const App = () => {
return <div><Form /></div>
}
You have to pass down the ref. For MUI TextField should do the following:
<Controller
control={methods.control}
name="fieldName"
render={({ field: { ref, ...field } }) => (
<TextField {...field} inputRef={ref} /> // ⬅️ The ref
)}
/>
A working demo: https://codesandbox.io/s/broken-microservice-xpuru
Does it work now ?
I'm building a website that uses "RTL (Right To Left) language", so I implemented react-hook-form, but react hook form uses only "LTR" forms like so...
In here I'm using the HTML attribute (dir="RTL") in my page, so all the text is "RTL" except the react hook form how can I fix this?.
import { TextField, Grid } from "#material-ui/core";
import { useFormContext, Controller } from "react-hook-form";
const FormInput = ({ name, label }) => {
const { control } = useFormContext();
return (
<Grid item xs={12} sm={6}>
<Controller
as={TextField} control={control} fullWidth name={name} label={label} required defaultValue=""/>
</Grid>
)
}
export default FormInput;
this is my FormInput component, I export this component to my address form component
import { useForm, FormProvider } from "react-hook-form";
<FormProvider>
<form>
<FormInput name="firstName" label="الإسم الأول (first name)" />
<FormInput name="lastName" label="اسم العائلة (last name)" />
</form>
</FormProvider>
this is my address form component.
I'm trying to use my custom TextField in my RegisterForm with Yup but he's dosnt work.
All time I have a message "⚠ Champ obligatoire." after click on Submit I don't understand why but is good with a simple input.
RegisterPage.js
import React, { useRef } from "react";
import { useForm } from "react-hook-form";
import Button from "../../lib/Button";
import TextField from "../../lib/TextField";
import * as yup from "yup";
const SignupSchema = yup.object().shape({
firstName: yup.string().required("⚠ Champ obligatoire."),
});
export default function Home() {
const { register, handleSubmit, errors, watch } = useForm({
validationSchema: SignupSchema,
});
const onSubmit = (data) => console.log(data);
console.log(errors);
return (
<div style={styles.inputForm}>
<p>Inscription</p>
<form style={{ marginTop: "40%" }} onSubmit={handleSubmit(onSubmit)}>
<label style={styles.label} htmlFor="firstName">
Prénom
</label>
<TextField
style={styles.input}
name="firstName"
placeholder="Toto"
type="text"
ref={register}
/>
<br />
{errors.firstName && (
<p style={styles.error}>{errors.firstName.message}</p>
)}
<br />
<Button
style={{ marginTop: 10 }}
type="submit"
onClick={handleSubmit(onSubmit)}>
Termine ton incription
</Button>
</form>
</div>
);
}
My CustomTextField
CustomTextfield.js
import React from "react";
import PropTypes from "prop-types";
import TextField from "#material-ui/core/TextField";
function CustomField({ InputLabelProps = {}, ...props }) {
return (
<TextField
InputLabelProps={{ shrink: true, ...InputLabelProps }}
{...props}
/>
);
}
CustomField.propTypes = {
classes: PropTypes.object.isRequired,
};
export default CustomField;
Thanks in advance!
You need to use inputRef instead of ref on TextField. ref will be applied to the outermost element which will be the div rendered by FormControl; and this won't be any help with the yup integration. inputRef will forward the ref to the input element.
<TextField
style={styles.input}
name="firstName"
placeholder="Toto"
type="text"
inputRef={register}
/>
Related documentation: https://material-ui.com/api/text-field/#props