Why I need to press twice times to submit my form? - javascript

I want to submit my login form, but I need to press twice times to accomplish login form, I don't know what happen.
I'm using styled components, this Button Form is an input tag
Also I'm using firebase-hooks
I just want to click one time and submit
Here my code
export default function LoginForm() {
// submit
const handleLogin = (e) => {
e.preventDefault();
signInWithEmailAndPassword(email, password);
if (user) {
setTimeout(() => {
history.push(`${ROUTES.HOME}`);
}, 1000);
}
};
if (loading) {
return <Loading />;
}
return (
<FormContainer onSubmit={(e) => handleLogin(e)}>
<LogoContainer>
<img
src="https://firebasestorage.googleapis.com/v0/b/auth-c068d.appspot.com/o/instagram%2Finstagram-signup.png?alt=media&token=cdafffb1-3034-474d-be96-d507b5e416c6"
alt="instagram-logo"
/>
</LogoContainer>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<ButtonForm type="submit" name="btn-login" value="Log In" />
<p>
Don't have an account? <Link to="/signup">Sign Up</Link>
</p>
{error && <Error errorMessage={error.message} />}
</FormContainer>
);
}

It looks like you will need to use a useEffect hook here. Your handleLogin function only runs once on each click. The first time you submit, user is undefined as expected. It works when you click it the second time because user is truthy.
Try removing this from the handleLogin function.
if (user) {
setTimeout(() => {
history.push(`${ROUTES.HOME}`);
}, 1000);
}
Then add a useEffect hook.
useEffect(() => {
if (user) {
setTimeout(() => {
history.push(`${ROUTES.HOME}`);
}, 1000);
}}, [user])
This effect will run on component mount, and after each time your user state is changed. (This is assuming your using react state for user, as I can't see where user is coming from)

change the type of button from type="submit" to type="button" and handle click using onCkick={() => clickHandler()}

Related

React redirects to the login page after entering username and password instead of redirecting to the required page

I'm creating a simple react application with 3 user roles. On the login page localhost:3000/login I have fields for username and password. Once those details are entered and the login button is clicked, the data is sent to the backend (running node js) and that data is used to query a MySql database. If the entered data matches a user in the database, the userId, name, password, and role is sent to the backend. This data is then sent to the front end. I can read the retrieved data from the front end and up to this point it works fine. However, when I'm trying to redirect a user according to the role, say the role is doctor and I want to redirect the user to localhost:3000/doctor , it goes to localhost:3000/doctor momentarily and switches to localhost:3000/login?. Shown below is the code for the login component.
import { useState } from "react";
import Axios from 'axios';
import { useNavigate } from 'react-router-dom'
import './login.css';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
let navigate = useNavigate()
const handleLogin = () => {
Axios.post("http://localhost:3001/login",
{
email: email,
password: password,
},
{
headers: {
'Content-Type': 'application/json'
}
}
)
.then((response) => {
console.log('response 1', response.data[0]['role'])
if (response.data[0]['role'] === 'doctor') {
navigate('/doctor');
}
});
};
return (
<div>
<form>
<h3>Electronic Prescription System</h3>
<h3>Login</h3>
<label>Email Address</label>
<input
className="inputs"
type="text"
placeholder="email"
onChange={(e) => {
setEmail(e.target.value)
}}
/>
<label>Password</label>
<input
className="inputs"
type="password"
placeholder="password"
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={handleLogin}>Log in</button>
</form>
</div>
)
};
export default Login;
If I remove all the code inside the handleLogin function and just have navigate('/doctor'); it redirects properly.
The routes are inside the Main component as shown below.
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import Login from "./pages/Login/Login";
import Doctor from "./pages/Doctor/Doctor";
import Patient from "./pages/Patient/Patient";
import Pharmacy from "./pages/Pharmacy/Pharmacy";
const Main = () => {
return (
<Routes>
<Route path="login" element={<Login />} />
<Route path="doctor" element={<Doctor />} />
<Route path="patient" element={<Patient />} />
<Route path="pharmacy" element={<Pharmacy />} />
</Routes>
);
}
export default Main;
The Doctor Component:
import { HeaderPanel } from '../../components/headerPanel/headerPanel'
import { PrescribePanel } from '../../components/prescribePanel/prescribePanel'
import { PrescriptionsList } from '../../components/prescriptionsList/prescriptionsList'
import './styles.css';
export const Doctor = () => {
return (
<>
<HeaderPanel />
<div className='wrapper'>
<PrescribePanel />
<PrescriptionsList />
</div>
</>
);
}
export default Doctor
I'm using react-router-dom version 6.6.1 and the react version is 18.2.0.
Tried using a useEffect hook to capture the role changing and redirecting, but id did not work either.
What I suspect is happening here is that the "log in" button is submitting the form which takes the default form action and reloads the page, the current route path being "/login". button elements have a type="submit" attribute value by default.
To resolve I'd suggest attaching handleLogin to the form element's onSubmit event handler and calling preventDefault on the onSubmit event object to prevent submitting the form and prevent reloading the page. This should allow the authentication logic to complete as expected.
Try to get yourself in the habit of being specific with the button element's type attribute.
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const navigate = useNavigate();
const handleLogin = (e) => { // <-- onSubmit event object
e.preventDefault(); // <-- don't take form action
Axios.post(
"http://localhost:3001/login",
{ email, password },
{
headers: {
'Content-Type': 'application/json'
}
}
)
.then((response) => {
if (response.data[0]['role'] === 'doctor') {
navigate('/doctor');
}
});
};
return (
<div>
<form onSubmit={handleLogin}> // <-- form onSubmit event handler
<h3>Electronic Prescription System</h3>
<h3>Login</h3>
<label>
Email Address
<input
className="inputs"
type="text"
placeholder="email"
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<label>
Password
<input
className="inputs"
type="password"
placeholder="password"
onChange={(e) => setPassword(e.target.value)}
/>
</label>
<button type="submit"> // <-- be specific with button type
Log in
</button>
</form>
</div>
);
};
export default Login;

How to get React state array to permanently update

I'm learning Node and React and now know how to integrate them and am working on making registration and login using Node and React. I'm going step by step, so currently I'm trying to at least get the inputs and put them into state as an array, and then after I get that I will go to hashing the password, sending the data to Node and the database, et cetera.
At the moment however, I'm a little bit stuck here. I'm trying to enter the username and password into my "details" state and then render it on the screen (or console log it, or whatever), but when I do it it shows up very quickly and then disappears. Why is the details state reverting to an empty array? How do I fix it? I did some research on here but couldn't figure it out.
import { useState } from 'react';
function App() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [details, setDetails] = useState([]);
const readUsername = (e) => {
setUsername(e.target.value);
}
const readPassword = (e) => {
setPassword(e.target.value);
}
const updateDetails = () => {
setDetails([username, password]);
}
return (
<div>
<h1>Register</h1>
<form>
<label htmlFor="username" name="username">Username: </label>
<input htmlFor="username" name="username" onChange={readUsername} />
<br/>
<label htmlFor="password" name="password">Password: </label>
<input htmlFor="password" name="password" type="password" onChange={readPassword} />
<br/>
<button onClick={updateDetails}>Submit</button>
</form>
<h1>{details}</h1>
</div>
);
}
export default App;
with the onChange handler on your input's, it is considered a "controlled" component. you also need to assign the value prop.
<input onChange={readUsername} value={username} />
Forms in React have the default behaviour as in HTML: refreshing the page upon submission.
React state only exists during the component's life. When you refresh the page, the component is unmounted, and the state is lost.
To prevent the page refresh, use a function to handle the form submission, and prevent the default behaviour.
const handleSubmit = (e) => {
e.preventDefault();
}
return (
...
<form onSubmit={handleSubmit}>
...
</form>
...
);
}
View demo on codesandbox
Further reading:
Forms in React
Preventing default behaviour of events
You want to create a controlled input so you should pass the value={password} attribute
like this:
<input htmlFor="username" name="username" onChange={readUsername} value={username} />
Also, I'd change how you handle to form. Change the button to type="submit"
like this
<button type="submit">Submit</button>
And then handle the submit from the <form>
like this:
<form onSubmit={(event) => updateDetails(event)}
And then on the function, you can use the submit event like this for example
const updateDetails = (e) => {
event.preventDefault()
...rest of logic
}

React The function called in onSubmit in a form is not run

I am trying to create a form in react that should take what the user typed into the input field and then when submitted, it should direct the user to a new page.
For example: if the user typed Mike, it should redirect the user to /Mike
but when I click submit all that happens is that the input is added to the url as a parameter like localhost:3000/?query=Mike
Regarding malicious input I will probably deal with that when I fix this issue.
My code is as follows
function search({ history }) {
const [query, setQuery] = useState([]);
const handleSubmit = (e) => {
history.push(`/${query}`);
};
const handleChange = (text) => (e) => {
setQuery(e.target.value);
};
return (
<>
<form
onSubmit={(e) => e.handleSubmit()}
className=""
>
<div className="">
<span className="">
<i className="fas fa-search"></i>
</span>
<input
onChange={handleChange("query")}
type="text"
name="query"
className=""
/>
</div>
<button type="submit">Search</button>
</form>
</>
);
}
Unfortunately it looks like as though the function specified in the onSubmit is never called I have tried this by using console.log. What it does is reload the page and add ?query=<INPUT> to the URL
How can I fix this issue so that it redirects to the new page
Inside <form>, you're doing onSubmit={(e) => e.handleSubmit()}. You're calling the handleSubmit() of the event object, not your own handleSubmit function. Instead, try this:
function search({ history }) {
const [query, setQuery] = useState([]);
const handleSubmit = (e) => {
history.push(`/${query}`);
e.handleSubmit(); // if you still want to actually submit
};
const handleChange = (text) => (e) => {
setQuery(e.target.value);
};
return (
<>
<form
onSubmit={handleSubmit}
className=""
>
<div className="">
<span className="">
<i className="fas fa-search"></i>
</span>
<input
onChange={handleChange("query")}
type="text"
name="query"
className=""
/>
</div>
<button type="submit">Search</button>
</form>
</>
);
}

Bootstrap 5 Alpha validation script on React

I am trying to work on bootstrap 5 alpha's validation on my react app.
So basically the form won't submit by default if they are left blank and will show either a check or an error mark at the bottom.
What I did so far is that I of course added the node packages for bootstrap 5 on my index.js which works fine. Next, I added this script tag to my public folder via BootstrapValidation.js file:
(function () {
const forms = document.querySelectorAll('.needs-validation')
Array.from(forms)
.forEach(function (form) {
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
And then inside my public folder's index.html I added it:
<script src="%PUBLIC_URL%/BootstrapValidation.js"></script>
When I check it on the source code I can see that it's loading it but when I added the required classes on my react component it doesn't show the errors nor prohibits it from submitting if there are blank fields:
const LoginScreen = ({ location, history }) => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const dispatch = useDispatch()
const userLogin = useSelector(state => state.userLogin)
const { loading, error, userInfo } = userLogin
const redirect = location.search ? location.search.split('=')[1] : '/'
useEffect(() => {
if(userInfo){
history.push(redirect)
}
}, [history, userInfo, redirect])
const submitHandler = (e) => {
e.preventDefault()
// DISPATCH LOGIN
dispatch(login(email, password))
}
return (
<>
<h1>Sign In</h1>
{ error && <Message variant='danger'>{error} </Message>}
{ loading && <Loader /> }
<form onSubmit={submitHandler} className="needs-validation" novalidate>
<div className="form-group">
<label for="email">Email address</label>
<input type="email" name="email" className="form-control" id="email" placeholder="Enter email" onChange={(e) => setEmail(e.target.value)} required/>
<div className="valid-feedback">Looks good!</div>
<div class="invalid-feedback"> Please supply a name.</div>
</div>
<div className="form-group">
<label for="password">Password</label>
<input type="password" name="password" className="form-control" id="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)} required/>
</div>
<button type="submit" className="btn btn-primary">Login</button>
</form>
<p>New User? <Link to={redirect ? `/register?redirect=${redirect}` : '/register' }>Register</Link></p>
</>
)
}
export default LoginScreen
Any idea what's causing this error or how can I properly execute this script so it will run as expected like on the documentation of bootstrap 5?
I think the main problem is that the form that is rendered by your component, has not rendered before the function in BootstrapValidation.js has run, meaning the classes that display the validation styles you want are not added to html.
Also you need to adjust the casing of properties like novalidate to noValidate and class to className, for to htmlFor, etc...
After you've done this you could move the logic of adding class names to run inside a useEffect hook like this:
useEffect(() => {
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.querySelectorAll(".needs-validation");
// Loop over them and prevent submission
Array.prototype.slice.call(forms).forEach(function (form) {
form.addEventListener(
"submit",
function (event) {
if (!form.checkValidity()) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add("was-validated");
},
false
);
});
}, []);

Trying to understand how formik package for react is constructed

I am in the process of learning React. I tried the Formik package https://www.npmjs.com/package/formik for React. It seems to work nice.
However, I tried to understand how it actually works with no luck.
Here is an example of simple form made with Formik:
import React from 'react';
import { Formik, Form, Field } from 'formik';
const FormikShort = () => (
<div>
<h1>Formik example</h1>
<Formik
initialValues={{email: '', password: ''}}
validate={values => {
let errors = {};
if (!values.email) {
errors.email = 'Required field';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
) {
errors.email = 'Not valid email address';
}
return errors;
}}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({ errors, touched, isSubmitting}) => (
<Form noValidate>
<div className="form-group">
<label htmlFor="emailLabel">Input email address</label>
<Field className="form-control" type="email" name="email" />
{errors.email && touched.email && errors.email}
</div>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
)}
</Formik>
</div>
);
export default FormikShort;
So in the example above there is an anonymous function call between Formik-element opening and closing tags. What makes this possible?
If I create my own simple component, such as
function Dummy(props) {
return <h1>Dummy component: {props.text}</h1>;
}
And then have it rendered like this
<Dummy
text="hello"
>
<div>Some text</div>
</Dummy>
the text "Some text" is not rendered at all. What do I have to do in order to be able insert stuff inside my own component and have it show up?
What about the function call inside the Formik-elements opening and closing tags? Where do the values for parameters (errors, touched, isSubmitting) come from?
I tried to look at the source files at node_modules/formik but I don't know exactly what file to look at. There are a lot of files, such as:
formik.cjs.development.js
formik.cjs.production.js
formik.esm.js
formik.umd.development.js

Categories