handleSubmit to call another function with event parameter - reactjs

This is React JS.
I had a nice working sendData function that creates a new record on my json file.
It worked nice until I decided to add useForm to add some yup resolvers.
Now in the <form> tag here is onSubmit={}.
If I write here
<form onSubmit={handleSubmit(sendData(), onSubmit)}>, I get the error and nothing works as before.
enter image description here
I except to understand how handleSubmit works and how to resolve this problem.
Thanks in advance, guys!
my code:
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import { Link, useNavigate } from 'react-router-dom';
import Confirmation from './Confirmation';
import * as yup from 'yup';
import { yupResolver } from '#hookform/resolvers/yup';
const schema = yup.object().shape({
name: yup.string().required(),
age: yup.number().positive().required(),
salary: yup.number().positive().required(),
email: yup.string().required(),
})
.required();
export default function LogIn() {
const { register, handleSubmit, formState: { errors }, } = useForm({
resolver: yupResolver(schema),
});
// for redirection
let navigate = useNavigate();
// modal for ghost mode
const [show, setShow] = useState(false);
const [details, setDetails] = useState({
name: '',
age: 0,
salary: 0,
email: ''
})
const sendData = async (event) => {
event.preventDefault()
const {name, age, salary, email} = details;
const res = await fetch("i hide the link :D",
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name, age, salary, email
})
})
navigate("/main");
}
const onSubmit = (data) => {
console.log(data)
}
return (
<div>
{show && <Confirmation show={show} setShow={setShow} />}
<div className="form-center">
<h1>Few Information</h1>
<form onSubmit={handleSubmit(sendData(), onSubmit)}>
<div className="form-controll">
<input type="text" {...register('name')} placeholder="Name"
onChange={(e) => setDetails({...details,name:e.target.value})}/>
{errors.name?.message && <p>{errors.name?.message}</p>}
<input type="number" {...register('age')} placeholder="Age"
onChange={(e) => setDetails({...details,age:e.target.value})}/>
{errors.age?.message && <p>{errors.age?.message}</p>}
<input type="number" {...register('salary')} placeholder="Salary in $"
onChange={(e) => setDetails({...details,salary:e.target.value})}/>
{errors.salary?.message && <p>{errors.salary?.message}</p>}
<input type="email" {...register('email')} placeholder="Email"
onChange={(e) => setDetails({...details,email:e.target.value})}/>
{errors.email?.message && <p>{errors.email?.message}</p>}
</div>
<div className="forgot">
Don't want to share data?<br></br>
<button onClick={() => {setShow(true)}}>Ghost mode</button>
</div>
<div className="btn">
<input type='submit' value='Go' />
</div>
</form>
</div>
</div>
)
}

handleSubmit function is a wrapper for react-hook-form to manage your data inputs, validation, errors, etc.. before calling your own sendData function.
Consider doing:
export default function LogIn() {
const sendData = async (data) => {
const {name} = data;
// your post request
}
return (
<form onSubmit={handleSubmit(sendData}> // remove the useless onSubmit
<input
type="text"
{...register('name')}
placeholder="Name"
// remove the onChange prop
/>
</form>
)
}

Related

React Hook Form ignores console.log onSubmit unless I use IIFE

For some reason, my function onSubmit ignores the console.log call but it does run my login function.
When I put the console.log inside an IIFE the data show on the console. Does anyone know the reason for this?
I am using NextJS 12 and React 18
Code:
import Button from 'components/Button'
import Form from 'components/Form'
import Input from 'components/Input'
import { useAuth } from 'contexts/UserContext'
import { NextPage } from 'next'
import { SubmitHandler, useForm } from 'react-hook-form'
import { zodResolver } from '#hookform/resolvers/zod'
import { string, z } from 'zod'
const schema = z.object({
email: string()
.min(1, 'Este campo é obrigatório')
.email('E-mail inválido')
.regex(
/([a-z0-9]{2,})(?:\.[a-z0-9]{2,})*#([a-z0-9]{2,})(?:\.[a-z0-9]{2,})+/,
'E-mail inválido'
),
password: string()
.min(1, 'Required')
.min(8)
.max(32)
})
type FormData = z.infer<typeof schema>
const Login: NextPage = () => {
const { login, loading } = useAuth()
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
defaultValues: { email: '', password: '' },
resolver: zodResolver(schema),
mode: 'all',
})
const onSubmit: SubmitHandler<FormData> = data => {
console.log(data) // Doesn't work ???
;(() => console.log(data))() // Works
login({ profile: data }) // Also works
}
return (
<>
<h1>Login</h1>
<Form onSubmit={handleSubmit(onSubmit)}>
<Input<FormData>
name="email"
label="E-mail"
type="email"
register={register}
errors={errors}
autoComplete="username"
required
/>
<Input<FormData>
name="password"
label="Senha"
type="password"
register={register}
errors={errors}
autoComplete="current-password"
pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*\W).{8,32}"
required
/>
<div className="control" data-control-type="buttons">
<Button variant="primary" type="submit" loading={loading}>
Enviar
</Button>
</div>
</Form>
</>
)
}

my form won't refresh back to initial state or navigate to the feeds page after success full registration i'm i wrong using async?

my form won't refresh back to initial state or navigate to the feeds page after success full registration and now react is telling me Async await is only available in es8 please can i go about this i want the form to provide some kind of feedback after registration like to n avigate to the homepage and clear all field but it's not working
import { Link, useNavigate } from 'react-router-dom'
import { toast } from 'react-toastify'
import {
getAuth,
createUserWithEmailAndPassword,
updateProfile,
} from 'firebase/auth'
import { setDoc, doc, serverTimestamp } from 'firebase/firestore'
import { db } from '../firebase.config'
import OAuth from '../components/OAuth'
function SignUp() {
const [formData, setFormData] = useState({
name: '',
email: '',
password: '',
})
const { name, email, password } = formData
const navigate = useNavigate()
const onChange = (e) => {
setFormData((prevState) => ({
...prevState,
[e.target.id]: e.target.value,
}))
}
const onSubmit = async (e) => {
e.preventDefault()
try {
const auth = getAuth()
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
)
const user = userCredential.user
updateProfile(auth.currentUser, {
displayName: name,
})
const formDataCopy = { ...formData }
delete formDataCopy.password
formDataCopy.timestamp = serverTimestamp()
await setDoc(doc(db, 'users', user.uid), formDataCopy)
navigate('/')
} catch (error) {
toast.error('Something went wrong with registration')
}
}
return (
<>
<div className='pageContainer'>
<header>
<p className='pageHeader'>Welcome Back!</p>
</header>
<form onSubmit={onSubmit}>
<input
type='text'
className='nameInput'
placeholder='Name'
id='name'
value={name}
onChange={onChange}
/>
<input
type='email'
className='emailInput'
placeholder='Email'
id='email'
value={email}
onChange={onChange}
/>
<div className='passwordInputDiv'>
<input
type='password'
className='passwordInput'
placeholder='Password'
id='password'
value={password}
onChange={onChange}
/>
</div>
<Link to='/forgot-password' className='forgotPasswordLink'>
Forgot Password
</Link>
<div className='signUpBar'>
<p className='signUpText'>Sign Up</p>
<button className='signUpButton'>
Sign Up
</button>
</div>
</form>
<OAuth />
<Link to='/sign-in' className='registerLink'>
Sign In Instead
</Link>
</div>
</>
)
}
export default SignUp

Two times click is necessary to Login in ReactJS

I am trying to make a Login page and I am successful in some way. So here is my Login component:
import React, { useState, useEffect } from "react";
import Axios from "axios";
import useForm from "../components/LoginForm/useForm";
import validate from "components/LoginForm/validate";
import redtruck from "../assets/img/red-truck.png";
import auth from "../Authentication/auth";
import { withRouter } from "react-router";
const Login = ({ submitForm, history }) => {
const [isSubmitted, setIsSubmitted] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const [login, setLogin] = useState(false);
async function submitForm() {
setIsSubmitted(true);
try {
await fetchLogin(values.email, values.password);
if(login){
auth.login(() => {
history.push("/admin");
});
}
} catch (e) {
auth.login(() => {
history.push("/");
})
}
}
const { handleChange, values, handleSubmit, errors } = useForm(
submitForm,
validate
);
useEffect(() => {
if (localStorage.getItem("user-info")) {
submitForm();
}
}, []);
const fetchLogin = async (email, password) => {
try {
setLoading(true);
const res = await Axios({
method: "POST",
url: `url`,
headers: {
},
data: {
user_email: email,
user_password: password,
},
});
if (res.status === 200) {
setLogin(true);
localStorage.setItem("user-info", JSON.stringify(res.data));
}
setLoading(false);
} catch (err) {
setError(err.message);
setLoading(false);
}
};
return (
<>
<div>
<div className="form-container">
<div className="form-content-left">
<img className="form-img" src={redtruck} alt="spaceship" />
</div>
<div className="form-content-right">
<h1>SIGN IN</h1>
<form className="form" onSubmit={handleSubmit}>
<div className="form-inputs">
<label htmlFor="email" className="form-label">
Email address
</label>
<input
id="signin-email"
type="email"
name="email"
placeholder="Enter email"
className="form-input"
value={values.email}
onChange={handleChange}
/>
{errors.email && <p>{errors.email}</p>}
</div>
<div className="form-inputs">
<label htmlFor="password" className="form-label">
Password
</label>
<input
id="signin-password"
type="password"
name="password"
placeholder="Password"
className="form-input"
value={values.password}
onChange={handleChange}
/>
{errors.password && <p>{errors.password}</p>}
{login ? "" : <p>The password or the email is wrong</p>}
</div>
<button
variant="primary"
type="submit"
className="form-input-btn"
>
LOGIN
</button>
</form>
</div>
</div>
</div>
</>
);
};
export default withRouter(Login);
So the login state is set to true when email and password are right for the user. Later I want to use it when redirecting page to "/admin". But my problem is I have to click twice to login in the first place. Besides I am not sure, if the catch part is right:
catch (e) {
auth.login(() => {
history.push("/");
})
}
So I would be really glad, if you can give me some hint about it.
Thanks...
it is not that you have to press twice, you can check component state, sometimes React batches setState and then update value. You can look at this setState doesn't update the state immediately

Form values coming as blank on submit

Not able to pick the values of email and token from the below form. It is displaying as blank during on click on submit. Can anyone please advise where I am doing wrong ?
https://codesandbox.io/s/dazzling-kirch-1gqq4?file=/src/App.js
import React, { useRef, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import Axios from "axios";
const CreateLink = () => {
const [email, setEmail] = useState("");
const [token, setToken] = useState("");
const {
handleSubmit,
register,
formState: { errors }
} = useForm();
const onSubmit = (e) => {
e.preventDefault();
alert(`Sending Email ${email}`);
const fetchData = async () => {
try {
const res = await Axios.post(
"http://localhost:8000/service/createlink",
email,
token
);
if (res.data.success) {
console.log("Link token created:" + res.data.success);
}
} catch (e) {
console.log(e);
}
};
fetchData();
};
return (
<div className="App">
<h1>Create Link</h1>
<form onSubmit={handleSubmit(onSubmit)} className="linkForm inputForm">
<div className="inputField">
<input
name="email"
placeholder="email"
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
{...register("email", {
required: "Email is required",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: "Invalid email address"
}
})}
/>
<span className="loginErrorTextFormat">
{errors.email && <p>{errors.email.message}</p>}
</span>
</div>
<div className="inputField">
<input
placeholder="token"
name="token"
type="text"
value={token}
onChange={(e) => setToken(e.target.value)}
{...register("token", {
required: "Token is required"
})}
/>
<span className="loginErrorTextFormat">
{errors.token && <p>Input is not valid</p>}
</span>
</div>
<input type="submit" />
</form>
</div>
);
};
export default CreateLink;
Because register return an object has property onChange. So it override your onChange, you just need move onChange after register
{...register("token", {
required: "Token is required"
})}
onChange={(e) => setToken(e.target.value)}

React redux First object missing on rendering when data is entered in the text field

What am I trying to do : Trying to enter the first name and last name of a person and display all the entered name below using redux global state management.
When I try to create a new object by entering data into the input fields, the 1st item is being displayed empty both on the view page and console log and from the 2nd item, all the items are being displayed correctly. I encountered this error previously but couldn't fix it.
Actual page:
import {useDispatch} from 'react-redux';
import {useState} from 'react';
const Inc = ()=>{
const[data, setData] = useState({});
const[firstName, setFirstName] = useState('');
const[lastName, setLastName] = useState('');
const dispatch = useDispatch();
const newData={
first_name: "",
last_name : ""
}
const handleFieldFirst=async (e)=>{
await setFirstName(e.target.value);
}
const handleFieldLast= async (e)=>{
await setLastName(e.target.value);
}
const handler = async (e)=>{
e.preventDefault();
newData.first_name = firstName ;
newData.last_name = lastName ;
await setData(newData);
console.log(data);
dispatch({type: 'INC', payload : data});
document.getElementById("form1").reset();
}
return(
<form id="form1" type ="submit" name="login" >
<input onChange={e => handleFieldFirst(e)} name="first" type="text" placeholder="First name"></input>
<input onChange={e => handleFieldLast(e)} name="last" type="text" placeholder="Last name"></input>
<button onClick={e => handler(e)}>Submit</button>
</form>
);
}
export default Inc;
View page
import {useSelector} from 'react-redux';
const Disp = ()=>{
const count = useSelector(state => state.counterReducer);
return(
<div>
{count.map((d) => <li key={d.last_name}>{d.first_name} {d.last_name}</li>)}
</div>
);
}
export default Disp;
enter image description here
First of all, you have to change your code. you have to use async and await only if the function returns a promise
import { useState } from "react";
import { useDispatch } from "react-redux";
const Inc = () => {
const [data, setData] = useState({
firstName: "",
lastName: "",
});
const dispatch = useDispatch();
const handler = (e) => {
e.preventDefault();
dispatch({ type: "INC", payload: data });
setData({
firstName: "",
lastName: "",
});
};
return (
<form id="form1" type="submit" name="login">
<input
onChange={(e) =>
setData({
...data,
firstName: e.target.value,
})
}
name="first"
type="text"
placeholder="First name"
value={data.firstName}
/>
<input
onChange={(e) =>
setData({
...data,
lastName: e.target.value,
})
}
name="last"
type="text"
placeholder="Last name"
value={data.lastName}
/>
<button onClick={handler}>Submit</button>
</form>
);
};
export default Inc;
by default useSelector will return the default state (you didn't attach reducer code so I'm assuming that the reducer returns default state) so add a condition to render <li>....</li> only the length of the array is greater than 0.

Resources