I have a straight forward react form below which called a handlesubmit function when the submit button is clicked. However, it seems that there are two issues. Firstly, the form doesn't validate inputs, and in fact accepts the empty inputs when submitted. Any help is appreciated please.
import Form from "react-bootstrap/Form"
import Button from "react-bootstrap/Button"
import Col from "react-bootstrap/Col"
import { useState } from "react"
const Register = () => {
const [validated, setValidated] = useState(false);
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const handleSubmit = (event) => {
const form = event.currentTarget;
console.log("in handlesubmit: " + form.checkValidity())
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
setValidated(true);
console.log("in handlesubmit: " + validated)
};
console.log('First Name: ' + firstName);
console.log('Last Name: ' + lastName);
return (
<div>
<h1>Registration page</h1>
<Form noValidated validated={validated} onSubmit={handleSubmit} style={{textAlign:'left'}}>
<Form.Row>
<Form.Group as={Col} controlId="formGridFirstName">
<Form.Label>First name</Form.Label>
<Form.Control type="text" placeholder="Enter first name" value={firstName} onChange={(e) => setFirstName(e.target.value)}/>
</Form.Group>
<Form.Group as={Col} controlId="formGridLastName">
<Form.Label>Last name</Form.Label>
<Form.Control type="text" placeholder="Enter last name" value={lastName} onChange={(e) => setLastName(e.target.value)}/>
</Form.Group>
</Form.Row>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</div>
)
}
export default Register
I think you probably need to add an onclick to your button. Try onClick={()=>{}} and see if that's triggers your form onsubmit?
Related
I have an antd form like this:-
<Form {...layout} form={form} name={formTitle(window.location.pathname)} onFinish={submitForm}>
<Form.Item name="name" label="Name" rules={NAME_VALIDATION}>
<Input value={name} onChange={({ target: { value, } }) => setName(value)}/>
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit">{formCardTitle(window.location.pathname)}</Button>
<Button htmlType="button" onClick={onReset}>
Reset
</Button>
</Form.Item>
</Form>
I want to disable the submit button on form submit. Something like this:-
const submitForm = async () => {
.................
.................
// disable submit button
}
I know how to disable the button within the form, something like this:-
<Button type="primary" htmlType="submit" disabled={!(name)}>
{formCardTitle(window.location.pathname)}
</Button>
But I want to disable the button only after form submit, inside the submitForm() function.
How can I do that?
You can use for that the Boolean
Here is the actual implementation in codesandbox
Here the one Example
import { useState } from "react";
const Form = () => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [btnDisable, setBtnDisable] = useState(false);
const emailHandler = (e) => setEmail(e.target.value);
const nameHandler = (e) => setName(e.target.value);
const submitHandler = (e) => {
e.preventDefault();
if (!name && !email.includes("#")) {
return;
}
const data = { name, email };
// send data to backend;
// after the sent data successfully
setBtnDisable(true);
//clear the input fields
setName("");
setEmail("");
};
return (
<form onSubmit={submitHandler}>
<input type="text" value={name} onChange={nameHandler} />
<input type="email" value={email} onChange={emailHandler} />
<button type="submit" disabled={btnDisable}>
submit
</button>
</form>
);
};
export default Form;
I wasn't sure how to phrase the title but basically, I am following a tutorial to create a login/registration and I am currently trying to display a message indicating whether the registration attempt was successful or not.
Here is my Register.js
import React, { useState } from 'react';
import { Form, Button } from 'react-bootstrap';
import axios from "axios";
export default function Register() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [register, setRegister] = useState(false);
const handleSubmit = (e) => {
// prevent the form from refreshing the whole page
e.preventDefault();
//set configuration
const configuration = {
method: "post",
url: "https://nodejs-mongodb-auth-app-learn.herokuapp.com/register",
data: {
email,
password,
},
};
// API call
axios(configuration)
.then((result) => {
console.log(result);
setRegister=(true);
})
.catch((error) => {
error= new Error;
})
};
return (
<>
<h2>Register</h2>
<Form onSubmit={(e)=>handleSubmit(e)}>
{/* email */}
<Form.Group controlId="formBasicEmail">
<Form.Label>Email Address</Form.Label>
<Form.Control
type="email"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email"
/>
</Form.Group>
{/* password */}
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter password"
/>
</Form.Group>
{/* submit button */}
<Button
variant="primary"
type="submit"
onClick={(e)=>handleSubmit(e)}
>
Register
</Button>
{/* display success message */}
{register ? (
<p className="text-success">You Are Registered Successfully</p>
) : (
<p className="text-danger">You Are Not Registered</p>
)}
</Form>
</>
)
};
The successful registration will log on the console, but either setRegister is not updating register to true, or my conditional statement is incorrect in some way?
It always shows "You Are Not Registered".
The correct way to ser an state using useState hook is:
e.g
const [register, setRegister] = useState(false);
setRegister(true)
I have this custom hook which supposed to make debounce email validation.
Suddenly I notice that it's not happening in all types.
Sometimes in the first types it's happen sometimes not.
See the log of "useEffect" which won't happen (for me - in each case) with any type. And when it's happening it's taking the previous value.
the Custom hook:
export function useDebounceEmailValidation(value, delay) {
console.log("useDebounceEmailValidation ? ")
// State and setters for debounced value
const [valid, setValid] = useState(true);
const initRun = useRef(true);
console.log("init run = " , initRun.current)
useEffect(
() => {
console.log("useEffect?"); //---------------------------> this not happening on each render
//we don't want to do it on initial running
if(initRun.current){
initRun.current = false;
}
else{
// Update debounced value after delay
const handler = setTimeout(() => {
console.log("validating mail - " ,value);
setValid(validateEmail(value));
// setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (also on delay change or unmount)
// This is how we prevent debounced value from updating if value is changed ...
// .. within the delay period. Timeout gets cleared and restarted.
return () => {
clearTimeout(handler);
};
}
},
[value, delay] // Only re-call effect if value or delay changes
);
return valid;
}
the form component:
import React, {useLayoutEffect, useRef, useState} from 'react';
import Button from 'react-bootstrap/Button';
import {useDebounceEmailValidation} from "./utils-hooks";
import {Alert, Col, Form} from "react-bootstrap";
export function SubscribersForm() {
const [details, setDetails] = useState({
firstName: "",
lastName: "",
email: "",
tel: ""
});
const [popupMsg, setPopupMsg] = useState("default");
const [showMsg, setShowMsg] = useState(false);
const [isError, setIsError] = useState(false);
const emailChange = useRef(false);
const [validated, setValidated] = useState(false);
//For cases we need to access the email through local state and not by state
// (the value will persist between component re-rendering and the reference updating won't trigger a component re-rendering)
const emailRef = useRef("");
const validEmail = useDebounceEmailValidation(emailRef.current, 600);
// // general layout effect - will be happen on each component update - for DEBUG
// useLayoutEffect(() => {
// console.log("details = ", details);
// });
//happening after change in the popup message
useLayoutEffect(() => {
setTimeout(() => {
//resetting the msg
setPopupMsg("");
setShowMsg(
false);
}, 2000);
}, [popupMsg])
//happen after change in the details
useLayoutEffect(() => {
//handling email changing (validation)
if (emailChange.current) {
emailRef.current = details.email;
console.log("email.current = " , emailRef.current)
}
}, [details]);
const handleChange = (ev) => {
ev.persist();
if (ev.target.name === "email" ) {
emailChange.current = true;
} else {
emailChange.current = false;
}
setDetails(prevDetails => ({
...prevDetails,
[ev.target.name]: ev.target.value
}));
}
const onSubmit = (ev) => {
const form = ev.currentTarget;
//The default validation for the form
if (form.checkValidity() === false || !validEmail) {
ev.preventDefault();
ev.stopPropagation();
setValidated(true);
return;
}
ev.preventDefault();
alert("Those are the details - you can send it from here to the server !!! :) \n\n" +
"name = " + details.firstName + " " + details.lastName
+ "\nemail = " + details.email
+ "\nphone = " + details.tel);
//we set validation to false, because by default you don't want to show validation
setValidated(false);
setPopupMsg("Your details have been successfully saved");
setIsError(false);
setDetails({
firstName: "",
lastName: "",
email: "",
tel: ""
});
setShowMsg(true);
}
return (
<div className="subscribers-input">
<h3>Subscribers - Form</h3>
<Form className="needs-validation" noValidate validated={validated}
onSubmit={onSubmit}>{/*start of the form block */}
<Form.Row className="">{/*start of the form row of 12/12 columns*/}
<Col xs={12} className="">
<Form.Group controlId="firstName" className="">
<Form.Control
type="text"
placeholder="First name"
value={details.firstName}
onChange={handleChange}
name="firstName"
required
size="sm"
aria-label="first name"
/>
</Form.Group>
</Col>
</Form.Row>
<Form.Row className="">
<Col xs={12} className="">
<Form.Group controlId="lastName" className="">
<Form.Control
type="text"
placeholder="Last name"
value={details.lastName}
onChange={handleChange}
name="lastName"
required
size="sm"
aria-label="last name"
/>
</Form.Group>
</Col>
</Form.Row>
<Form.Row className="">
<Col xs={12} className="">
<Form.Group controlId="email" className="">
<Form.Control
type="email"
placeholder="Email"
value={details.email}
onChange={handleChange}
name="email"
required
size="sm"
aria-label="email"
isInvalid={!validEmail}
/>
<Form.Control.Feedback type="invalid">Email is Invalid</Form.Control.Feedback>
</Form.Group>
</Col>
</Form.Row>
<Form.Row className="">
<Col xs={12} className="">
<Form.Group controlId="tel" className="">
<Form.Control
type="tel"
placeholder="Phone"
value={details.tel}
onChange={handleChange}
name="tel"
required
size="sm"
aria-label="phone"
/>
</Form.Group>
</Col>
</Form.Row>
{showMsg &&
<Alert variant={isError ? 'danger' : 'success'}>{popupMsg}</Alert>
}
<Button type="submit" size="sm">Save</Button>
</Form>
</div>
)
}
See how the log not always happening.
there are two things to know: first, custom hooks only rerun when the component that we use our custom hook in (in our case, "SubscribersForm"), rerenders. second, the useEffect dependency array checks the equality of objects by references.
so to be sure that useEffect can intercept changes in its dependency array object we should pass new references for that object.
in the main component "SubscribersForm" you pass a reference to your "useDebounceEmailValidation" custom hook, so when you change the value of emailRef.current, no rerenders happen, and also the reference of the object is the same as before. you should use states in these cases
I have a database on AtlasDB cloud service. In my React application, I want my code to save the data from the form below to be saved in the database, however I get internal server error (500) when I make a post request. What could be the problem here? The code of the React component is as follows:
import React, { useState, useEffect } from "react";
//state for form fields
import { Link } from "react-router-dom";
import { Form, Button, Row, Col } from "react-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import Message from "../components/Message";
import Loader from "../components/Loader";
import FormContainer from "../components/FormContainer";
import { register } from "../actions/userActions";
const RegisterScreen = ({ location, history }) => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [message, setMessage] = useState(null);
const dispatch = useDispatch();
const userRegister = useSelector((state) => state.userLogin);
const { loading, error, userInfo } = userRegister;
const redirect = location.search ? location.search.split("=")[1] : "/";
const goToLoginScreen = () => {
history.goBack();
}
useEffect(() => {
if (userInfo) {
history.push(redirect);
}
}, [history, userInfo, redirect]);
const submitHandler = (e) => {
e.preventDefault();
//dispatch register
if(password !== confirmPassword){
setMessage('Passwords do not match');
} else {
goToLoginScreen();
dispatch(register(name, email, password));
}
};
return (
<FormContainer>
<h1>Sign Up</h1>
{message && <Message variant="danger">{message}</Message>}
{error && <Message variant="danger">{error}</Message>}
{loading && <Loader />}
<Form onSubmit={submitHandler}>
<Form.Group controlId="name">
<Form.Label>Name </Form.Label>
<Form.Control
type="name"
placeholder="Enter name"
value={name}
onChange={(e) => setName(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="email">
<Form.Label>Email Address</Form.Label>
<Form.Control
type="email"
placeholder="Enter email"
value={email}
onChange={(e) => setEmail(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
placeholder="Enter password"
value={password}
onChange={(e) => setPassword(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="confirmPassword">
<Form.Label>Confirm Password</Form.Label>
<Form.Control
type="password"
placeholder="Confirm password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
></Form.Control>
</Form.Group>
<Button type="submit" variant="primary">
Register
</Button>
</Form>
<Row className="py-3">
<Col>
Have an account?{" "}
<Link to={redirect ? `/login?redirect=${redirect}` : "/login"}>
Login
</Link>
</Col>
</Row>
</FormContainer>
);
};
export default RegisterScreen;
It is very difficult to me to localize the error in code, whether it is a question of front-end or back-end. The user is not saved in the remote database.
I'm trying to write tests for the Login component of my app, and want to test that the user can enter email and password fields, then submit them. There are two complications: it has a to another component, and it uses context to import a function called loginUser. The first problem I solve by making a custom renderWithRouter function, but I can't solve the second problem because the component isn't able to consume the loginUser function outside of context. I get this error: Error: Uncaught [TypeError: Cannot read property 'then' of undefined] because loginUser is undefined. Any idea how I can solve this? Here is Login.tsx:
import React, { useState } from 'react';
import { useAuth } from 'context/authContext'
import { useHistory, Link } from 'react-router-dom'
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button'
export const Login = (props: any) => {
const { setUser, loginUser } = useAuth()
const [email, setEmail] = useState<string>();
const [password, setPassword] = useState<string>();
const [errors, setErrors] = useState<boolean>()
const history = useHistory()
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
loginUser({
email,
password
}).then((result: any) => {
console.log(result)
if (result.message === 'login error') {
setErrors(true)
} else {
history.push('/dashboard/home')
}
})
}
return(
<div className="login-wrapper">
<h1>Please Log In</h1>
<div className='form-wrapper'>
<Form onSubmit={handleSubmit}>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control
isInvalid={errors ? true : false}
type="email"
placeholder="Enter email"
onChange={e => setEmail(e.target.value)}/>
<Form.Control.Feedback type="invalid">
{errors ? 'either you don\'t have an account or the email and password do not match' : null}
</Form.Control.Feedback>
<Form.Text className="text-muted">
umm..
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
placeholder="Password"
onChange={e => setPassword(e.target.value)}
/>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
<div>
<Link to='/register'>new user? click here to register</Link>
</div>
</Form>
</div>
</div>
)
}
And Login.test.tsx:
test("allows user to input their email", () => {
const onSubmit = jest.fn();
renderWithRouter(<Login />)
const input = screen.getByLabelText("Email address")
const pwd = screen.getByLabelText("Password")
const button = screen.getByText("Submit")
fireEvent.change(input, { target: { value: "t#t.com"}})
fireEvent.change(pwd, { target: { value: "123456"}})
fireEvent.click(button)
expect(onSubmit).toBeCalled()
})
})
Haven't run it but refactor to something like this and then test the LoginForm component.
import React, { useState } from 'react';
import { useAuth } from 'context/authContext'
import { useHistory, Link } from 'react-router-dom'
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button'
interface LoginFormProps {
onSubmit: (email: string, password: string) => void;
errors: boolean;
}
export const LoginForm: React.FC<LoginFormProps> = ({onSubmit, errors}) => {
const [email, setEmail] = useState<string>();
const [password, setPassword] = useState<string>();
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
onSubmit(email, password)
}
return(
<div className="login-wrapper">
<h1>Please Log In</h1>
<div className='form-wrapper'>
<Form onSubmit={handleSubmit}>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control
isInvalid={errors ? true : false}
type="email"
placeholder="Enter email"
onChange={e => setEmail(e.target.value)}/>
<Form.Control.Feedback type="invalid">
{errors ? 'either you don\'t have an account or the email and password do not match' : null}
</Form.Control.Feedback>
<Form.Text className="text-muted">
umm..
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
placeholder="Password"
onChange={e => setPassword(e.target.value)}
/>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
<div>
<Link to='/register'>new user? click here to register</Link>
</div>
</Form>
</div>
</div>
)
}
export const Login = (props: any) => {
const { setUser, loginUser } = useAuth()
const [errors, setErrors] = useState<boolean>(false)
const history = useHistory()
const handleSubmit = (email, password) => {
loginUser({
email,
password
}).then((result: any) => {
console.log(result)
if (result.message === 'login error') {
setErrors(true)
} else {
history.push('/dashboard/home')
}
})
}
return(
<LoginForm onSubmit={handleSubmit} errors={errors} />
)
}