Formik form does not recognize Material ui Button - reactjs

I am building a login form with Formik with Materail UI, but Formik doesn't recognize Button of Material UI. If I replace the Button with html button everything works. Could anybody explain why it's not working with the Button component. Below is my code:
import React, { ReactElement } from "react";
import TextField from "#material-ui/core/TextField";
import FormControl from "#material-ui/core/FormControl";
import { Formik, Form } from "formik";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
const useStyles = makeStyles((theme) => ({
root: {
margin: theme.spacing(1),
width: 200,
display: "flex",
},
input: {
marginTop: 5,
marginBottom: 5,
},
}));
interface Props {}
export default function loginForm({}: Props): ReactElement {
const classes = useStyles();
return (
<Box className={classes.root}>
<Formik
initialValues={{ username: "", password: "" }}
validate={(values) => {
const errors: { username?: string; password?: string } = {};
if (!values.username) {
errors.username = "Required";
} else if (!values.password) {
errors.password = "Required";
}
return errors;
}}
onSubmit={(values, { setSubmitting }) => {
console.log("values: ", values);
setSubmitting(false);
}}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
}) => (
<form onSubmit={handleSubmit}>
<FormControl>
<TextField
className={classes.input}
error={errors.username ? true : false}
type="username"
name="username"
onChange={handleChange}
onBlur={handleBlur}
value={values.username}
label="Username"
variant="outlined"
helperText={
errors.username && touched.username && errors.username
}
/>
<TextField
className={classes.input}
error={errors.password ? true : false}
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
label="Password"
variant="outlined"
helperText={
errors.password && touched.password && errors.password
}
/>
</FormControl>
<Box display="flex" justifyContent="space-between">
<Button
variant="contained"
color="primary"
disabled={isSubmitting}
>
Submit
</Button>
<Button
variant="contained"
color="secondary"
disabled={isSubmitting}
>
Cancel
</Button>
</Box>
</form>
)}
</Formik>
</Box>
);
}
please note the code above doesn't work, but if you replace Button with button, the form works.

In most browsers, a HTML button by default has the type=submit which means that Formik's submit handler will be called. A Material-UI button does not have this default so the submit handler will never be called. Try adding type=submit to your <Button> props.
(Also, check out Formik's Material-UI integration examples)

You should add the id to the form.
<form onSubmit={handleSubmit} id="myForm">
And bind the form id with submit button
<Button
type="submit"
form="myForm"
>

Related

How to submit Formik when button submit outside tag formik in ReactJs?

I have a component composition as above, the box with brown color is the parent and has a blue child box as the formix container and the right side with the green color is the container where the button is placed. is it possible to submit a form with a button outside the formix tag?
I read the documentation but still not found the solution.
you can handle it using the Formik tag innerRef reference. I added a working demo pls find here.
import React, { useRef } from "react";
import { render } from "react-dom";
import { Formik } from "formik";
import * as Yup from "yup";
import "./helper.css";
const App = () => {
const formRef = useRef();
const handleSubmit = () => {
if (formRef.current) {
formRef.current.handleSubmit();
}
};
return (
<div className="app">
<Formik
innerRef={formRef}
initialValues={{ email: "" }}
onSubmit={async (values) => {
await new Promise((resolve) => setTimeout(resolve, 500));
alert(JSON.stringify(values, null, 2));
}}
validationSchema={Yup.object().shape({
email: Yup.string().email().required("Required")
})}
>
{(props) => {
const {
values,
touched,
errors,
handleChange,
handleBlur,
handleSubmit
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email" style={{ display: "block" }}>
Email
</label>
<input
id="email"
placeholder="Enter your email"
type="text"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.email && touched.email
? "text-input error"
: "text-input"
}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
</form>
);
}}
</Formik>
<button type="submit" onClick={handleSubmit}>
Submit
</button>
</div>
);
};
render(<App />, document.getElementById("root"));

How to check validation field required in Form when onClick button

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

Antd Form with React hook form

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>

Submitting data to Firestore from formik in react

I'm trying to figure out how to submit form data to firestore from a formik form in a react app.
I've used this tutorial to try to learn how to make the form and then tried to add the firebase form submission to the submit handler.
The form has:
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import firebase from '../../../../firebase';
// import firestore from '../../../../firebase';
import { withStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
// import axios from 'axios';
import {
Formik, Form, Field, ErrorMessage,
} from 'formik';
import * as Yup from 'yup';
// import { DisplayFormikState } from './formikHelper';
const styles = {
};
function Contact(props) {
const { classes } = props;
const [open, setOpen] = useState(false);
const [isSubmitionCompleted, setSubmitionCompleted] = useState(false);
function handleClose() {
setOpen(false);
}
function handleClickOpen() {
setSubmitionCompleted(false);
setOpen(true);
}
return (
<React.Fragment>
<Link
// component="button"
className="footerlinks"
onClick={handleClickOpen}
>
Contact
</Link>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="form-dialog-title"
>
{!isSubmitionCompleted &&
<React.Fragment>
<DialogTitle id="form-dialog-title">Contact Us</DialogTitle>
<DialogContent>
<DialogContentText>
Thanks for your interest.
</DialogContentText>
<Formik
initialValues={{ email: '', name: '', comment: '' }}
onSubmit={(values, { setSubmitting }) => {
setSubmitting(true);
firebase.firestore.collection("contact").doc.set({
name: "name",
email: "email",
comment: "comment"
})
// axios.post(contactFormEndpoint,
// values,
// {
// headers: {
// 'Access-Control-Allow-Origin': '*',
// 'Content-Type': 'application/json',
// }
// },
.then(() => {
setSubmitionCompleted(true);
});
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.email()
.required('Required'),
name: Yup.string()
.required('Required'),
comment: Yup.string()
.required('Required'),
})}
>
{(props) => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset,
} = props;
return (
<form onSubmit={handleSubmit}>
<TextField
label="Name"
name="name"
className={classes.textField}
value={values.name}
onChange={handleChange}
onBlur={handleBlur}
helperText={(errors.name && touched.name) && errors.name}
margin="normal"
style={{ width: "100%"}}
/>
<TextField
error={errors.email && touched.email}
label="Email"
name="email"
className={classes.textField}
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
helperText={(errors.email && touched.email) && errors.email}
margin="normal"
style={{ width: "100%"}}
/>
<TextField
label="Let us know how we can help"
name="comment"
className={classes.textField}
multiline
rows={4}
value={values.comment}
onChange={handleChange}
onBlur={handleBlur}
helperText={(errors.comment && touched.comment) && errors.comment}
margin="normal"
style={{ width: "100%"}}
/>
<DialogActions>
<Button
type="button"
className="outline"
onClick={handleReset}
disabled={!dirty || isSubmitting}
>
Reset
</Button>
<Button type="submit" disabled={isSubmitting}>
Submit
</Button>
{/* <DisplayFormikState {...props} /> */}
</DialogActions>
</form>
);
}}
</Formik>
</DialogContent>
</React.Fragment>
}
{isSubmitionCompleted &&
<React.Fragment>
<DialogTitle id="form-dialog-title">Thanks!</DialogTitle>
<DialogContent>
<DialogContentText>
Thanks
</DialogContentText>
<DialogActions>
<Button
type="button"
className="outline"
onClick={handleClose}
>
Back to app
</Button>
{/* <DisplayFormikState {...props} /> */}
</DialogActions>
</DialogContent>
</React.Fragment>}
</Dialog>
</React.Fragment>
);
}
export default withStyles(styles)(Contact);
The firestore config file has:
When I try this, I get a warning in the console when I press submit (no error messages and the form just hangs with data that I entered in the form.
instrument.ts:129 Warning: An unhandled error was caught from
submitForm() TypeError:
firebase__WEBPACK_IMPORTED_MODULE_2_.default.firestore.collection is not a function
Note, I've dried both .doc.set and .doc.add in the firestore method - neither works.
Found it eventually -the doc has to be doc()

How to validate multiple emails using one validation schema field in Formik?

I have a customer form which takes two values.
Customer name
Customer Emails (Which can be multiples)
I've given an add button besides the email field through which user can add more emails to the form. now i want to validate each email that's been added. also if it's added it is required too. empty email is not allowed.
The question is i have only one validation schema to validate email field. how can i use the same field to validate multiple emails?
Even after adding the correct email. it still gives error !
Please see the sandbox link to see the code. code sandbox link
Here is the working code with multiple email validation and errors.
I've used Formik FieldArray to handle multiple emails.
You can replace your code with this in your sandbox to test.
import React from "react";
import { TextField, IconButton } from "#material-ui/core";
import {
AddCircleOutlined as AddCircleOutlinedIcon,
IndeterminateCheckBox as IndeterminateCheckBoxIcon
} from "#material-ui/icons";
//FORMIK
import { Formik, FieldArray, getIn, ErrorMessage } from "formik";
import * as Yup from "yup";
export default function UnitsDrawer(props) {
const callAPI = e => {
console.log(e.name);
console.log(e.email);
};
const testSchema = Yup.object().shape({
name: Yup.string().required("Customer name is required"),
email: Yup.array().of(
Yup.string()
.email("Enter a valid email")
.required("Email is required")
)
});
const initialValues = {
name: "",
email: [""]
};
const formRef = React.useRef();
return (
<div>
<Formik
innerRef={formRef}
validationSchema={testSchema}
initialValues={initialValues}
onSubmit={(values, actions) => {
actions.setSubmitting(false);
callAPI(values);
}}
>
{({
handleChange,
handleBlur,
values,
errors,
touched,
handleSubmit,
isSubmitting,
}) => {
return (
<>
<div>
<TextField
label="Customer Name"
name="name"
margin="normal"
variant="outlined"
error={errors.name && touched.name}
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
fullWidth
/>
<ErrorMessage name="name" component="div" />
</div>
<FieldArray name="email">
{({ push, remove }) =>
values.email.map((field, index) => (
<div key={`${index}`} className="dynamic-fields">
<div>
<TextField
label="Email"
variant="outlined"
className="input-item"
error={
getIn(touched, `email.${index}`) &&
getIn(errors, `email.${index}`)
}
name={`email.${index}`}
value={values.email[index]}
onChange={handleChange}
onBlur={handleBlur}
fullWidth
/>
<ErrorMessage name={`email.${index}`} component="div" />
</div>
<IconButton
aria-label="filter list"
className="add-icon"
onClick={() => {
push("");
}}
>
<AddCircleOutlinedIcon color="primary" />
</IconButton>
{values.email.length > 1 && (
<IconButton
aria-label="filter list"
className="add-icon"
onClick={() => {
remove(index);
}}
>
<IndeterminateCheckBoxIcon />
</IconButton>
)}
</div>
))
}
</FieldArray>
</>
);
}}
</Formik>
</div>
);
}

Resources