i'm quite new with react and i'm building a form with react-hook and useState to manage my datas after the submit.
I'm not able to use textfield as they are blocked. I think that i make some errors into value/onChange parameters but i don't know what type of error.
import React, { useState } from "react";
import {
TextField,
MenuItem,
Typography,
Checkbox,
Divider,
Button,
} from "#mui/material";
import { MdError } from "react-icons/md";
import { BsArrowRight } from "react-icons/bs";
import "../style/contactform.scss";
import { useForm } from "react-hook-form";
const initialState = {
name: "",
email: "",
};
const ContactForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const [state, setState] = useState(initialState);
const { name, email } = state;
const handleInputChange = (e) => {
const { name, value } = e.target;
setState({ ...state, [name]: value });
};
const onSubmit = (e) => {
e.preventDefault();
console.log("form submit");
setState(initialState);
};
return (
<form className="contact-form" onSubmit={handleSubmit(onSubmit)}>
<Typography variant="h4" className="form-title">
Be the first.
</Typography>
<div className="form-component">
<TextField
id="standard-basic"
label="Nome*"
variant="standard"
name="nome"
value={name}
onChange={handleInputChange}
{...register("nome", {
required: true,
})}
/>
{errors?.nome?.type === "required" && (
<MdError className="form-validation-icon" />
)}
</div>
<Divider className="form-hr" />
<div className="form-component">
<TextField
id="standard-basic"
label="Email*"
variant="standard"
name="email"
value={email}
onChange={handleInputChange}
{...register("email", {
required: true,
pattern: {
value:
/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
},
})}
/>
{errors?.email?.type === "required" && (
<MdError className="form-validation-icon" />
)}
{errors?.email?.type === "pattern" && (
<Typography variant="p" className="form-validation-email">
Inserisci un indirizzo email valido.
</Typography>
)}
</div>
<Divider className="form-hr" />
<Button className="form-submit" type="submit" variant="contained">
<BsArrowRight />
</Button>
</form>
);
};
export default ContactForm;
Textfields are completely block but initial state is actually working, do i miss something?
Can you help me?
To assign initial values using the useForm hook, you pass it under the defaultValues parameter passed to the hook like so:
const {
register,
handleSubmit,
reset
formState: { errors },
} = useForm({
defaultValues: initialState
});
Then just pass the ...register name and email to the inputs. There is no need to assign values to them again:
<TextField
id="standard-basic"
label="Name*"
variant="standard"
name="name"
{...register("name", {
required: true,
})}
/>
// for the email..
<TextField
id="standard-basic"
label="Email*"
variant="standard"
name="email"
{...register("email", {
required: true,
pattern: {
value: /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,},
})}
/>
If you'll notice, the values are off the text fields already and there's also no need for the handleInputChange function. The useForm hook takes care of that.
Edit:
In addition to the onSubmit function, the handleSubmit from useForm passes a data object into the function like this:
const onSubmit = (data) => {
console.log("form submitted", data);
reset(); // this can be destructured of the `useForm` hook.
};
For more info check their documentation
Related
I have a problem with react-hook-form and reactstrap. I have this component List.jsx:
import { useContext, useEffect } from "react";
import { ListContext, ADD_LIST } from '../providers/ShoppingListProvider';
import { Link } from "react-router-dom";
import { useForm } from 'react-hook-form';
import { ListGroup, ListGroupItem, Form, FormGroup, Button, Input, Label, Container, Row, Col } from 'reactstrap';
export const Lists = () => {
const [store, dispatch] = useContext(ListContext);
const { register, handleSubmit, formState: { errors }, getValues } = useForm();
const onSubmit = data => addList(data);
const addList = (data) => {
console.log(data);
//dispatch({ type: ADD_LIST, store: store, listName: data.name });
}
return (
<Container>
<Row>
<Col>
<ListGroup>
{store.lists.map((item, key) => {
return <ListGroupItem key={key} ><Link to={"/list/" + key}>{item.name}</Link></ListGroupItem>
})}
</ListGroup>
</Col>
<Col>
<Form onSubmit={handleSubmit(onSubmit)}>
<FormGroup>
<Label >Name of new list</Label>
<Input type="text" placeholder="name of list" {...register("name", { required: true })} ></Input>
</FormGroup>
<Button type="submit">Přidat list</Button>
</Form>
</Col>
</Row>
</Container>
);
}
the problem is, that when I submit the form, nothing happens (addList won't happen). But when I replace Input (from reactstrap) with normal input from classic html, everything seems working. So the problem is Input from reactstrap. Does anyone knows how to fix this issue?
Thank you very much!
Try like this, look at the innerRef in input
const { register, handleSubmit, formState: { errors } } = useForm();
const { ref, ...rest } = register('name')
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Input type="text" placeholder="Name" innerRef={ref} {...rest} />
<Input type="submit" />
</form>
);
I am using jest and testing library to test a form. But the mock function is not being called. And the last line expect(mockOnSubmit).toHaveBeenCalled(); always fails.
Here's my test code:
App.test.js
import { render, fireEvent, cleanup } from "#testing-library/react";
import App from "./App";
import { act } from "react-dom/test-utils";
afterEach(cleanup);
test("email and password field are clear for submit", async () => {
const mockOnSubmit = jest.fn();
const { getByPlaceholderText, getByText } = render(
<App onSubmit={mockOnSubmit} />
);
const emailNode = getByPlaceholderText(/email/i);
const passwordNode = getByPlaceholderText(/password/i);
const testEmail = "test#example.com";
const testPassword = "123123";
await act(async () => {
fireEvent.change(emailNode, {
target: { value: testEmail }
});
fireEvent.change(passwordNode, {
target: { value: testPassword }
});
});
expect(emailNode.value).toBe(testEmail);
expect(passwordNode.value).toBe(testPassword);
const submit = getByText(/submit/i);
await act(async () => {
fireEvent.click(submit);
});
expect(mockOnSubmit).toHaveBeenCalled();
});
App.js
import { useForm } from "react-hook-form";
import { Controller } from "react-hook-form";
export default function App({ onSubmit = (data) => console.log(data) }) {
const { control, handleSubmit } = useForm();
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="email"
control={control}
defaultValue=""
rules={{
required: `Email is required.`,
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: "Invalid email address"
}
}}
render={({ field }) => {
return (
<input
placeholder="Email"
type="text"
key="email"
name="email"
id="email"
{...field}
/>
);
}}
/>
<Controller
name="email"
control={control}
defaultValue=""
rules={{
required: `Password is required.`
}}
render={({ field }) => {
return (
<input
placeholder="Password"
type="text"
key="password"
name="password"
id="password"
{...field}
/>
);
}}
/>
<input type="submit" value="submit" />
</form>
);
}
This minimal implementation is also available at
https://codesandbox.io/s/react-hook-form-testing-olo4i
Sidenote: This works fine without react-hook-form implementation. But as I add handleSubmit from react-hook-form it does not work anymore. Here's a working version by #Florian Motteau where it is working by bypassing react-hook-form
You are checking that the onSubmit props has been called, but in your component your are not calling this props when the form is submitted. So the mock you provide in the test will never get called. Besides this, in App.js, the onSubmit props is passed an evaluation of handleSubmit (handleSubmit()), instead you must pass a reference to the handler function (handleSubmit, or () => handleSubmit()).
Solving these two problems you get :
// App.js
export default function App({ onSubmit = (data) => console.log(data) }) {
const { control, handleSubmit } = useForm();
return (
<form onSubmit={onSubmit}>
<Box sx={{ mt: 2 }}>
<EmailField label="Email" name="email" control={control} />
</Box>
<Box sx={{ mt: 2 }}>
<PasswordField label="Password" name="password" control={control} />
</Box>
<Box sx={{ mt: 2 }}>
<Button name="submit" type="submit" variant="contained">
Submit
</Button>
</Box>
</form>
);
}
I also suggets that you use visual text to interact with DOM elements :
fireEvent.click(getByText("Submit"));
Fixed sandbox : https://codesandbox.io/s/react-hook-form-testing-forked-vs9ez?file=/src/App.js
Finally, you should not have to wrap things in act(), react-testing-library will do this for you, see https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning.
I have been trying to implement a login page functionality in react using Ant Desing forms.
I keep getting the following error
I tried checking all the semicolons to see if this is due to a missing semicolon but was unable to find the root cause.
TypeError: e.preventDefault is not a function
at onSubmit (LoginForm.js:27)
at onFinish (LoginForm.js:44)
at onFinish (Form.js:73)
at useForm.js:811
Here is my code for the login page.
import React, {useState} from 'react';
import {Link, Redirect} from 'react-router-dom';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import styles from './NormalLoginForm.css';
import { Form, Input, Button, Checkbox, Layout, Card } from 'antd';
import { UserOutlined, LockOutlined } from '#ant-design/icons';
import {login} from '../auth/actions/auth';
const NormalLoginForm = ({ login, isAuthenticated }) => {
const [formData, setFormData] = useState({
username: '',
password:''
});
const { username, password} = formData;
const onChange = e => setFormData({...formData, [e.target.name]: e.target.value});
const onFinish = (values) => {
console.log('Received values of form onFinish: ', values);
// login(username,password)
};
const onSubmit = e => {
e.preventDefault();
console.log('Received values of form(onSumbit):' );
login(username,password);
};
if (isAuthenticated)
return <Redirect to='/' />;
return (
<Form
name="normal_login"
className="login-form"
initialValues={{
remember: true,
}}
onFinish={e=> onSubmit(e)}
style={{ maxWidth: 300, align: 'center'}}
>
<Form.Item
name="username"
rules={[
{
required: true,
message: 'Please input your Username!',
},
]}
>
<Input prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="Username"
onChange={e=> onChange(e)}
/>
</Form.Item>
<Form.Item
name="password"
rules={[
{
required: true,
message: 'Please input your Password!',
},
]}
>
<Input
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="Password"
onChange={e => onChange(e)}
/>
</Form.Item>
<Form.Item>
<Form.Item name="remember" valuePropName="checked" noStyle>
<Checkbox>Remember me</Checkbox>
</Form.Item>
<a className="login-form-forgot" href="">
Forgot password
</a>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
{/*Or register now!*/}
</Form.Item>
</Form>
);
};
NormalLoginForm.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
};
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated
});
connect(mapStateToProps, { login })(NormalLoginForm);
function LoginForm () {
return (
<NormalLoginForm/>
)
}
export default LoginForm;
Where am I making the mistake? this is my first project in React, been stuck here for weeks. Kindly help.
If you want to call preventDefault when the form gets submitted, use an onSubmit handler instead. onFinish's parameter is not the event, but the values.
onSubmit={e => e.preventDefault()}
onFinish={onFinish}
const onFinish = (values) => { // you can remove this parameter if you don't use it
login(username, password);
};
this is my React worksection.js file and its function made instead of class
export default function WorkSection() {
now i need to do here constructor to initialise state and do function operations which i'll call on button click
return (
<div className={classes.section}>
<GridContainer justify="center">
<GridItem cs={12} sm={12} md={8}>
<h2 className={classes.title}>Work with us</h2>
<h4 className={classes.description}>
BAIOS BAY LLP is looking for collaborations with you, Contact us
today !
</h4>
<form onSubmit={this.handleSubmit}>
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="Your Name"
id="name"
onChange={this.handleChange}
defaultValue={this.state.name}
formControlProps={{
fullWidth: true
}}
/> </GridItem> </GridContainer>
</div>
);
}
this is my form where i am submitting name and will add button click so how can i initialise state and functions to call onclick functions where
my functions are as :
constructor(props) {
super(props);
this.state = {
name : ''}
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
handleClick = event => {
this.setState({
[event.target.id]: event.target.checked
});
}
handleSubmit = event => {
event.preventDefault();
if (this.state.username === '') {
this.showMessage('Info', "Username is empty");
return;
}
}
i need to place this function and i did it with class worksection but how to do it with export default function Worksection()
The thing you're probably looking for called react hooks. They allow you to use state management in your functional components. They're cool because they're lightweight in compare with class components.
First, import useState function from react:
import { useState } from 'react'
Then, before your return, add these lines:
const [name, setName] = useState('');
The first argument here is the name of your state property, and the second one is the function to change it.
So, instead of this:
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
Write this:
handleChange = event => {
setName(event.target.value);
}
If you want to make it more complex, you can rewrite your hook from this:
const [name, setName] = useState('');
to this:
const [state, setState] = useState({
name: '',
checked: false,
});
export default function WorkSection() {
const { register, handleSubmit } = useForm();
const onSubmit = data => {
axios
.get("
...................... my code ...............
and my input form is :
return (
<div className={classes.section}>
<form onSubmit={handleSubmit(onSubmit)}>
<Input
name="Name"
placeholder="Name"
inputRef={register}
fullWidth={true}
/>
<Input
name="email"
type="email"
placeholder="Email"
fullWidth={true}
inputRef={register({ required: true })}
/>
<Input
name="contact"
placeholder="Contact"
fullWidth={true}
inputRef={register({ required: true })}
/>
<Input
name="description"
placeholder="Message"
multiline={true}
fullWidth={true}
inputRef={register({ required: true })}
/>
<button className="btnColor" justify="center" type="submit">
Send Message
</button>
</GridItem>
</GridContainer>
</form>
</div>
Basically i used
inputRef={register}
and other part as satated above.
Right code , worked for me ~
I am using react-validation to validate a login form. After submitting the form I want to know has an error or not. I can get the error state inside my custom button object in below way.
const customSubmit = ({ hasErrors, ...props }) => {
return <Button disabled={hasErrors} >Login</Button>
{hasErrors.toString()}/>
};
const SubmitButton = button(customSubmit);
Is there any way to get hasError state when submit a from?
I can share a similar code when I use State and handleChange to get the errors in the button.
First you need a function were you declare the useState.
import React from "react";
import { Box, Button, Collapse, Grid, TextField } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
function FormVal() {
const [phone, setPhone] = React.useState<string>();
const [errors, setErrors] = React.useState<{ phone: string }>();
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const {
target: { value },
} = event;
setErrors({ phone: "" });
setPhone(value);
let reg = new RegExp(/^\d*$/).test(value);
if (!reg) {
setErrors({ phone: "Only numbers" });
}
};
Now in the return section you need to declare the handleChange. There may be some elements that you don't know such as and that is because I am using Material-ui.
return (
<Box className={classes.root} width="100%" padding="5px">
<Grid container spacing={3}>
<Grid item xs={12}>
<TextField
id="outlined-basic"
autoComplete="off"
value={phone}
label="phone number"
inputProps={{ maxLength: 255 }}
onChange={handleChange}
required
error={Boolean(errors?.phone)}
helperText={errors?.phone}
variant="outlined"
/>
</Grid>
<Collapse in={phone?.length! > 0}>
<div style={{ width: "100%" }}>
<Button variant="contained">Save</Button>
</div>
</Collapse>
</Grid>
</Box>
);
}
export default FormVal;