Hi I am trying to combine React hook form, material UI, Yup validation but I just cant make it work, RHF and MUI works together but not with yup and RHF and YUP works but not with MUI.
anyway here is my work until now the only thing that is not working is that, the handleSubmit never fires and when I type something it gives me a uncontrolled error
import axios from "axios";
import styles from "../../styles/RegisterPage.module.css";
import TextField from "#mui/material/TextField";
import { Container } from "#mui/material";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "#hookform/resolvers/yup";
import * as yup from "yup";
const schema = yup.object({
age: yup
.number()
.positive()
.integer()
.typeError("Amount must be a number")
.required(),
});
const MedicalHistoryPage = () => {
const {
handleSubmit,
control,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
});
const onSubmit = (data)=>{
console.log(data)
}
return (
<>
<Container maxWidth="md" className={styles.container}>
<form
onSubmit={handleSubmit(onSubmit)}
>
<div className={styles.title}>Ενημέρωσε το ιατρικό ιστορικό σου</div>
<div className={styles.form}>
<div className={styles.formsContainer}>
<div className={styles.input}>
<Controller
control={control}
name="age"
render={({ field }) => (
<TextField
{...field}
fullWidth
type="Number"
label="Input your age"
variant="outlined"
defaultValue=""
helperText={errors.age?.message}
error={!!errors.age?.message}
/>
)}
/>
</div>
</div>
<div className={styles.buttonsContainer}>
<div className={styles.loginButton}>
<input type="submit" className={styles.buttonStyle} />
</div>
</div>
</div>
</form>
</Container>
</>
);
};
export default MedicalHistoryPage;
Thanks for your time
Related
I have a Formik form that uses Yup for validation. One of my fields is a Datepicker, but I am unable to integrate it into yup validation.
Below is code which works as far as rendering the component, but as soon as I try wrap <DatePicker/> inside <Field name="date></Field> tags, nothing renders.
const [fieldDate,setFieldDate] = useState ("");
const dateSchema = Yup.object().shape({
date: Yup.date().required('Date is required'),
});
const initialValues = {date:''};
return (
<div>
<FormContainer>
<Formik
initialValues={initialValues}
//validationSchema={loginValidationSchema}
validationSchema={dateSchema}
onSubmit={()=>{console.log ("ok")}}
>
{({ isSubmitting, values, setFieldValue, handleChange, handleBlur, errors, touched }) => (
<Form className="form">
<LocalizationProvider dateAdapter={DateFnsUtils}>
<DatePicker
label="Date"
value={fieldDate}
onChange={(newValue:any) => {
setFieldDate(newValue);
}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<div className="buttonWrapper">
<SubmitButton type="submit" className="SubmitButton">Submit</SubmitButton>
</div>
</Form>
)}
</Formik>
</FormContainer>
</div>
And here are my imports...I'm including them because I find that there are multiple libraries with the same name from MUI that have different parent directories with different requirements which has somewhat added to my confusion when trying to solve this via online solutions:
import React, { useState} from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import {FormContainer,FieldContainer, SubmitButton } from "../GlobalStyle";
import { TextField } from "#mui/material";
import * as Yup from "yup";
import "react-datepicker/dist/react-datepicker.css";
import { LocalizationProvider } from '#mui/x-date-pickers/LocalizationProvider';
import DateFnsUtils from "#date-io/date-fns";
import { DatePicker } from '#mui/x-date-pickers/DatePicker';
Thanks!
import React from "react";
import { Formik, Form, Field, } from "formik";
import { TextField } from "#mui/material";
import * as Yup from "yup";
import { LocalizationProvider } from '#mui/x-date-pickers/LocalizationProvider';
import DateFnsUtils from "#date-io/date-fns";
import { DatePicker } from '#mui/x-date-pickers/DatePicker';
const dateSchema = Yup.object().shape({
date: Yup.date().required('Date is required'),
});
const initialValues = { date: '' };
export const App = () => {
return (
<div>
<Formik
initialValues={initialValues}
validateOnMount
validationSchema={dateSchema}
onSubmit={() => {
console.log('ok');
}}
>
<Form className="form">
<Field name="date">
{({ field, meta, form }) => (
<LocalizationProvider dateAdapter={DateFnsUtils}>
<DatePicker
label="Date"
renderInput={(params) => <TextField {...params} />}
// Get your date value from your form
value={field.value}
// Set your date in the form
onChange={(date) => form.setFieldValue('date', date)}
/>
{/* Error from yup */}
<div>{meta.error}</div>
</LocalizationProvider>
)}
</Field>
<div className="buttonWrapper">
<button type="submit" className="SubmitButton">
Submit
</button>
</div>
</Form>
</Formik>
</div>
);
};
export default App;
I'm currently in the process of creating a simple form using MUI TextFields, Formik and some Yup validation.
I ran into some issues with performance with lower spec devices when scrolling and I have extracted stuff like MUI styles and other components such as toastify notifications.
I am relatively new to react, but I have used MUI + Formik + Yup before with a much more complex form without any lag when I scroll and I now can't seem to fix it. The documentation for MUI has also been extremely confusing with many different versions, depracated use cases and so on. I figured useMemo() could work in this instance as there is sure to be some excessive re-rendering, but I am not sure how to implement it.
I'll gladly give more info if needed.
import React from "react";
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import {emailSuccess, emailError, emailSubmitting} from "../lib/toasts";
import {useSubmit} from "../lib/useSubmit";
import {postJson} from "../lib/http";
import {useStyles} from "../lib/inputStyle";
import {TextField} from "#mui/material";
import SendRoundedIcon from "#mui/icons-material/SendRounded";
import { useFormik } from "formik";
import * as Yup from "yup";
export default function Contact () {
const toastId = React.useRef(null);
const classes = useStyles();
const formik = useFormik({
initialValues: {
name: '',
email: '',
message: ''
},
validationSchema: Yup.object({
name: Yup
.string()
.max(255)
.required(
'Name is required'),
email: Yup
.string()
.email(
'Must be a valid email')
.max(255)
.required(
'E-mail address is required'),
message: Yup
.string()
.max(1200)
.required(
'You cannot send an empty message')
}),
onSubmit: () => {
//This should be optional... Please fix!
}
});
const { handleSubmit: handleEmail, submitting, error } = useSubmit(
async () => {
emailSubmitting(toastId);
await postJson("/api/mail", {
name: formik.values.name,
email: formik.values.email,
message: formik.values.message,
});
},
() => {
emailSuccess(toastId);
},
);
if (error) {
emailError(toastId)
}
return (
<div className="contact">
<div className="section">
<div className="contact-innerRef"/>
<h4>Contact</h4>
<div className="contact-card">
<h5>Get In Touch</h5>
<hr/>
<form className="contact-form" action="" method="post" encType="text/plain">
<TextField className={classes.root}
error={Boolean(formik.touched.name && formik.errors.name)}
helperText={formik.touched.name && formik.errors.name}
label="Name"
margin="normal"
name="name"
onChange={formik.handleChange}
value={formik.values.name}
variant="outlined"
/>
<TextField className={classes.root}
error={Boolean(formik.touched.email && formik.errors.email)}
helperText={formik.touched.email && formik.errors.email}
label="Email"
margin="normal"
name="email"
onChange={formik.handleChange}
value={formik.values.email}
variant="outlined"
/>
<TextField className={classes.root}
error={Boolean(formik.touched.message && formik.errors.message)}
helperText={formik.touched.message && formik.errors.message}
label="Message"
margin="normal"
name="message"
onChange={formik.handleChange}
value={formik.values.message}
variant="outlined"
multiline
rows={7}
/>
</form>
<button onClick={handleEmail} disabled={!formik.isValid || !formik.dirty || submitting}
className="btn">
<SendRoundedIcon height="50%"/>
</button>
<ToastContainer />
</div>
</div>
</div>
);
}```
I am new to React and especially formik. I am trying to learn how to create a login form using formik that will display a label called email with the email input. A label called password with the password input and a button called submit.
My problem is that only the two inputs and submit button displays in the browser. The two labels for email and password do not display in the browser. Please advise how I can fix this.
App.js:
import React from 'react';
import './App.css';
import FormikContainer from './components/FormikContainer';
import LoginForm from './components/LoginForm';
function App() {
return (
<div>
<LoginForm />
</div>
);
}
export default App;
LoginForm.js:
import React from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import FormikContainer from './FormikContainer';
import FormikControl from './FormikControl';
function LoginForm() {
const initialValues = {
email: '',
password: ''
};
const validationschema = Yup.object({
email: Yup.string().email('invalid email format').required('Requird'),
password: Yup.string().required('Required')
});
const onSubmit = (values) => {
console.log('Form data', values);
};
return (
<div>
<Formik
initialValues={initialValues}
validationschema={validationschema}
onSubmit={onSubmit}
>
{(formik) => {
return (
<Form>
<FormikControl
control="input"
type="email"
label="Email"
name="email"
/>
<FormikControl
control="input"
type="password"
label="Password"
name="password"
/>
<button type="submit" disabled={!formik.isValid}>
Submit
</button>
</Form>
);
}}
</Formik>
</div>
);
}
export default LoginForm;
FormikControl.js:
import React from 'react';
function FormikControl(props) {
const { control, ...rest } = props;
switch (control) {
case 'input':
return <input {...rest} />;
case 'textarea':
case 'select':
case 'radio':
case 'checkbox':
case 'date':
default:
return null;
}
return <div></div>;
}
export default FormikControl;
FormikContainer.js
import React from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import FormikControl from './FormikControl';
function FormikContainer() {
const initialValues = {
email: ''
};
const validationschema = Yup.object({
email: Yup.string().required('Required')
});
const onSubmit = (values) => console.log('Form data', values);
return (
<div>
<Formik
initialValues={initialValues}
validationschema={validationschema}
onSubmit={onSubmit}
>
{(formik) => (
<Form>
<FormikControl
control="input"
type="email"
label="Email"
name="email"
/>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);
}
export default FormikContainer;
I would like to do easily this instead :
function FormikControl({ control, id, label, ...rest }) {
return (
<>
{control === "input" && <label htmlFor={id}>{label}</label>}
<input id={id} {...rest} />
</>
);
}
export default FormikControl;
I using EXTReact and have TextField
Here is my code.
<Textfield
required
label="Required Field"
requiredMessage="This field is required."
errorTarget="under"
name="field"
onChange={(e) => handleChaneValue(e)}
/>
<Button
ui="confirm"
text="BTN submit"
handler={handleClick}
style={{border: '1px solid black'}}
/>
When the submit Button is clicked, if the field does not have any value it should show the error message.
You should:
- step 1 : When textfField change, update the state of your component (with the value of textfield) ==> Two ways binding will be necessary (Data binding in React)
- step 2 : Then, you create your error message div/text/paragraph anyway (where you want and with the style you need), and you add style display:none. Here you just set the html and css of the feature.
- step 3 : Then, on click (button), you check the state value of textField (the one you made at step 1). If it's empty, you change style of the error message div ==> display:block
Please refer to the following code.
Note: The code is only looking for a single TextField. I have used material-ui TextField and Button component(I am not sure which one you're using). However, the logic should remain the same.
import React, { useState } from 'react';
import TextField from '#mui/material/TextField';
import Button from '#mui/material/Button';
function App() {
const [field, setField] = useState('');
const [error, setError] = useState(false);
const handleClick = () => {
if (!field) {
setError(true);
return null;
}
};
const handleChangeValue = (e) => {
setField(e.target.value);
};
return (
<div>
<TextField
required
label="Required Field"
value={field}
error={!!error}
name="field"
onChange={(e) => handleChangeValue(e)}
helperText={error ? 'this is required' : ''}
/>
<Button onClick={handleClick} style={{ border: '1px solid black' }}>
Submit
</Button>
</div>
);
}
export default App;
You can use Formik Formik-official.
import React from 'react';
import ReactDOM from 'react-dom';
import { useFormik } from 'formik';
import * as yup from 'yup';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField';
const validationSchema = yup.object({
email: yup
.string('Enter your email')
.email('Enter a valid email')
.required('Email is required'),
password: yup
.string('Enter your password')
.min(8, 'Password should be of minimum 8 characters length')
.required('Password is required'),
});
const WithMaterialUI = () => {
const formik = useFormik({
initialValues: {
email: 'foobar#example.com',
password: 'foobar',
},
validationSchema: validationSchema,
onSubmit: (values) => {
alert(JSON.stringify(values, null, 2));
},
});
return (
<div>
<form onSubmit={formik.handleSubmit}>
<TextField
fullWidth
id="email"
name="email"
label="Email"
value={formik.values.email}
onChange={formik.handleChange}
error={formik.touched.email && Boolean(formik.errors.email)}
helperText={formik.touched.email && formik.errors.email}
/>
<TextField
fullWidth
id="password"
name="password"
label="Password"
type="password"
value={formik.values.password}
onChange={formik.handleChange}
error={formik.touched.password && Boolean(formik.errors.password)}
helperText={formik.touched.password && formik.errors.password}
/>
<Button color="primary" variant="contained" fullWidth type="submit">
Submit
</Button>
</form>
</div>
);
};
ReactDOM.render(<WithMaterialUI />, document.getElementById('root'));
I am trying to use antd form with react-hook-form but couldn't get it to work.
Basically I am using a theme with antd and want to integrate react-hook-form for better validation and other things. So far this is the code
import { Button, Form, Input, Message, Row, Tooltip } from "antd";
import { useForm, Controller } from "react-hook-form";
import {
EyeTwoTone,
MailTwoTone,
PlaySquareTwoTone,
QuestionCircleTwoTone,
SkinTwoTone,
} from "#ant-design/icons";
import Link from "next/link";
import Router from "next/router";
import styled from "styled-components";
const FormItem = Form.Item;
const Content = styled.div`
max-width: 400px;
z-index: 2;
min-width: 300px;
`;
const Register = (props) => {
const defaultValues = {
name: null,
phone: null,
email: null,
password: null,
confirmPassword: null,
checked: false,
};
const { handleSubmit, reset, watch, control, errors, getValues } = useForm({
defaultValues,
});
// const { register, errors, handleSubmit, control } = useForm({
// mode: 'onChange',
// });
const onSubmit = async (data) => {
try {
console.log("data", data);
} catch (err) {
console.log("err", err);
}
};
return (
<Row
type="flex"
align="middle"
justify="center"
className="px-3 bg-white"
style={{ minHeight: "100vh" }}
>
<Content>
<div className="text-center mb-5">
<Link href="/signup">
<a className="brand mr-0">
<PlaySquareTwoTone style={{ fontSize: "32px" }} />
</a>
</Link>
<h5 className="mb-0 mt-3">Sign up</h5>
<p className="text-muted">create a new account</p>
</div>
<Form layout="vertical" onSubmit={handleSubmit(onSubmit)}>
<Controller
as={
<FormItem
label="Email"
name="email"
rules={[
{
type: "email",
message: "The input is not valid E-mail!",
},
{
required: true,
message: "Please input your E-mail!",
},
]}
>
<Input
prefix={<MailTwoTone style={{ fontSize: "16px" }} />}
type="email"
placeholder="Email"
/>
</FormItem>
}
control={control}
name="select"
/>
{/* <Input inputRef={register} name="input" /> */}
<button type="button" onClick={() => reset({ defaultValues })}>
Reset
</button>
<input type="submit" />
</Form>
</Content>
</Row>
);
};
export default Register;
Now, as you can see I am having regular form tag and inside that the input field. But using the regular form, I am not getting the layout props provided by the antd forms. Also I am not able to get the values during submit.
So my question is that how can I use AntD form component with react hook form so i can use the benefits of react-hook0form as well a Antd styling.
You can simply use the built-in useForm method of ant design, no need to pull in a thirdparty. It looks like:
const [form] = Form.useForm();
Also Form has onFinish method not onSubmit in ant, at least in version 4.
Use controller wrapper from react-hook-form, documentation link
Import Controller
import { useForm, Controller } from 'react-hook-form';
Call control from useForm
const { handleSubmit, control } = useForm();
Your Input
<Form.Item label="Email">
<Controller
name="email"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Input onChange={onChange} value={value} />
)}/>
</Form.Item>