Form is not updating when using hooks - reactjs

When using React Hooks my inputs are not updating. It means that when I'm updating the input in HTML, the formData is like:
{username: "", password: "", "": "a", undefined: undefined}. What's happening?
export default function Login() {
const [formData, setFormData] = useState({
username: "",
password: "",
});
async function handleSubmit(event: FormEvent) {
event.preventDefault();
const body = {
username: (event.currentTarget as any).username.value,
password: (event.currentTarget as any).password.value,
};
const res = await fetch("/api/login", {
method: "POST",
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
},
});
if (res.ok) {
Router.push("/");
}
}
function handleChange(event: FormEvent<HTMLInputElement>) {
setFormData((prevState) => ({
...prevState,
[event.currentTarget?.name]: event.currentTarget?.value,
}));
}
return (
<Layout>
<form onSubmit={handleSubmit}>
<FormInput
label="User name"
placeholder="Enter your username"
type="text"
value={formData.username}
onChange={handleChange}
/>
<FormInput
label="Password"
placeholder="Enter your password"
type="password"
value={formData.password}
onChange={handleChange}
/>
<input type="submit" value="Send" />
<Link href="/register">Create account</Link>
</form>
</Layout>
);
}
FormInput component is just:
type Props = {
label: string;
} & React.HTMLProps<HTMLInputElement>;
export default function FormInput({ label, ...props }: Props) {
return (
<>
<label htmlFor={label}>{label}</label>
<input {...props} />
</>
);
}

You need to implement handleChange as a callback and add the name prop. Use event.target;
export default function Login() {
const [formData, setFormData] = useState({
username: "",
password: "",
});
const handleSubmit = (event: FormEvent) => {
event.preventDefault();
const body = {
username: (event.currentTarget as any).username.value,
password: (event.currentTarget as any).password.value,
};
const res = await fetch("/api/login", {
method: "POST",
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
},
});
if (res.ok) {
Router.push("/");
}
}
const handleChange(event: FormEvent<HTMLInputElement>) => {
setFormData((prevState) => ({
...prevState,
[event.target?.name]: event.target?.value,
}));
}
return (
<Layout>
<form onSubmit={handleSubmit}>
<FormInput
label="User name"
placeholder="Enter your username"
type="text"
value={formData.username}
onChange={handleChange}
name="username"
/>
<FormInput
label="Password"
placeholder="Enter your password"
type="password"
value={formData.password}
onChange={handleChange}
name="password"
/>
<input type="submit" value="Send" />
<Link href="/register">Create account</Link>
</form>
</Layout>
);
}

Related

How can I submit to custom credentials?

Hello I have the following next auth configuration:
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
export default NextAuth({
providers: [
CredentialsProvider({
name:'Login',
credentials: {
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" }
},
async authorize(credentials){
try{
const response = await fetch(`http://localhost:5001/auth/login`,{
method: "POST",
body: JSON.stringify(credentials),
headers: {'Content-type': 'application/json'}
})
const token = (await response).json()
if (response.ok && token) {
return token
}
} catch (error) {
console.log(error)
}
return null
}
})
],
})
and I have the following page:
import { getProviders, signIn } from "next-auth/react"
export default function Singin({ providers:{credentials} }) {
return (
<form method="post" action={credentials.callbackUrl}>
<label>
Email address
<input type="email" id="email" name="email" />
</label>
<label>
Password
<input type="password" id="password" name="password" />
</label>
<button type="submit">Sign in with Email</button>
</form>
)
}
export async function getServerSideProps(context) {
const providers = await getProviders()
return {
props: { providers },
}
}
I used credentials.callbackUrl in order to send the form data to the credentials but doesn't work and next-auth redirects to that page instead of make the auth.
How can I set this page?
Thanks
Try to call the signIn method and pass the credentials to it:
import { signIn } from 'next-auth/react';
export default function Singin() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await signIn('creadentials', {
email,
password,
});
};
return (
<form method='post' onSubmit={handleSubmit}>
<label>
Email address
<input
type='email'
id='email'
name='email'
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<label>
Password
<input
type='password'
id='password'
name='password'
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</label>
<button type='submit'>Sign in with Email</button>
</form>
);
}

React-Formik: How to create registeration form using Formik?

I am learning React.js and want to use Formik for my project. In backend I've created registration endpoint, which works fine in frontend as well with simple form. I don't know how to implement Formik instead of simple form. I can't move forward, since all the time i get the same error:
Line 121:20: 'firstName' is not defined no-undef
Line 122:19: 'lastName' is not defined no-undef
Line 123:16: 'email' is not defined no-undef
Line 124:19: 'password' is not defined no-undef
How to get rid of that error and make it work?
Here is the register component with default form, which works fine.
import React, { useState } from 'react'
const Register = () => {
const [data, setData] = useState([])
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const saveRegister = () => {
fetch('http://localhost:8000/api/v1/user', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstName: firstName,
lastName: lastName,
email: email,
password: password,
}),
})
.then((res) => res.json())
.then((result) => {
setData(result)
console.log(result)
})
.catch((err) => console.log('error'))
}
const handleFirstName = (e) => {
setFirstName(e.target.value)
}
const handleLastName = (e) => {
setLastName(e.target.value)
}
const handleEmail = (e) => {
setEmail(e.target.value)
}
const handlePassword = (e) => {
setPassword(e.currentTarget.value)
}
const handleSubmit = (e) => {
e.preventDefault()
saveRegister()
setFirstName('')
setLastName('')
setEmail('')
setPassword('')
}
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
name="firsName"
onChange={handleFirstName}
value={firstName}
placeholder="firstName"
/>
<input
type="text"
name="lastName"
onChange={handleLastName}
value={lastName}
placeholder="lastName"
/>
<input
type="text"
name="email"
onChange={handleEmail}
value={email}
placeholder="email"
/>
<input
type="text"
name="password"
onChange={handlePassword}
value={password}
placeholder="password"
/>
<button type="submit">signup</button>
</form>
</div>
)
}
export default Register
In here I'm trying to implement Formik instead
import React, { useState } from 'react'
import {
Formik,
Form,
Field,
ErrorMessage,
} from 'formik'
import * as Yup from 'yup'
function Register() {
const [data, setData] = useState([])
const saveRegister = (values) => {
fetch('http://localhost:8000/api/v1/user', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstName: values.firstName,
lastName: values.lastName,
email: values.email,
password: values.password,
}),
})
.then((res) => res.json())
.then((result) => {
setData(result)
})
.catch((err) => console.log('error'))
}
const initialValues = {
email: '',
lastName: '',
firstName: '',
password: ''
}
const onSubmit = (values, setSubmitting) => {
setSubmitting(true);
console.log(values);
saveRegister(values)
setSubmitting(false)
}
const validationSchema = Yup.object({
firstName: Yup.string().required('Required'),
lastName: Yup.string().required('Required'),
password: Yup.string().required('Required'),
email: Yup.string()
.email('Invalid email format').required('Required'),
})
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
>
{({ values, isSubmitting }) => (
<Form >
<div>
<label htmlFor='email'>E-mail</label>
<Field
type='text'
id='email'
name='email'
/>
<ErrorMessage name='firstName' />
</div>
<div>
<label htmlFor='firstName'>firstName</label>
<Field
type='text'
id='firstName'
name='firstName'
/>
<ErrorMessage name='lastName' />
</div>
<div>
<label htmlFor='lastName'>lastName</label>
<Field
type='text'
id='lastName'
name='lastName'
/>
<ErrorMessage name='lastName' />
</div>
<div >
<label htmlFor='password'>password</label>
<Field
type='password'
id='password'
name='password'
/>
<ErrorMessage name='password' />
</div>
<button type='submit'>Submit</button>
</Form>
)}
</Formik>
)
}
export default Register
<>
<Formik {{ initialValues, validationSchema, onSubmit }}>
</Formik>
</>
and your on submit method
const onSubmit = (values, setSubmitting) => {
setSubmitting(true);
console.log(values);
saveRegister(values)
setSubmitting(false)
and in your saveRegister
const saveRegister = (values) => {
values.email //and so on
}

How to change Input fields values in React?

I am not being able to change input fields.Any suggestion?
const MiddlePanel = ({ user }) => {
const history = useHistory();
const [data, setData] = useState({
firstname: '',
lastname: '',
email: '',
phoneNo: '',
});
const { firstname, lastname, email, phoneNo } = data;
useEffect(() => {
const fetchUser = async () => {
const res = await axios.get(`http://localhost:5000/users/${user._id}`);
setData({
firstname: res.data.firstname,
lastname: res.data.lastname,
email: res.data.email,
password: res.data.password,
});
};
fetchUser();
}, [user._id]);
const handleChange = (e) => {
setData({ ...data, [e.target.name]: e.target.value });
};
const handleSubmit = async (e) => {
e.preventDefault();
const newUser = { firstname, lastname, email, phoneNo };
try {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify(newUser);
await axios.patch(
`http://localhost:5000/users/${user._id}`,
body,
config
);
history.push("/");
} catch (err) {}
};
return (
<div>
<Form onSubmit={handleSubmit}>
<Input
type="text"
name="firstname"
onChange={handleChange}
value={user.firstname}
/>
<Input
type="text"
name="lastname"
onChange={handleChange}
value={user.lastname}
/>
<Input
type="email"
name="email"
onChange={handleChange}
value={user.email}
/>
<Input
type="tel"
name="phoneNo"
onChange={handleChange}
value={user.phoneNo}
/>
<PrimaryButton className="btn btn-primary" style={{ width: "100%" }}>
Update
</PrimaryButton>
</Form>
</div>
);
};
I think you should use data.firstname instead of user for the value property
<div>
<Form onSubmit={handleSubmit}>
<Input
type="text"
name="firstname"
onChange={handleChange}
value={data.firstname}
/>
<Input
type="text"
name="lastname"
onChange={handleChange}
value={data.lastname}
/>
<Input
type="email"
name="email"
onChange={handleChange}
value={data.email}
/>
<Input
type="tel"
name="phoneNo"
onChange={handleChange}
value={data.phoneNo}
/>
<PrimaryButton className="btn btn-primary" style={{ width: "100%" }}>
Update
</PrimaryButton>
</Form>
</div>

React: How do I carry authentication from login page to redirect page?

In my React project, users log in and then are redirected to the page '/registergig'. This is a protected page that only logged in users should be able to access.
On the register gig page, users are required to input text data that is posted to a firebase database. However when I try and submit data, I get a 403 error returned:
index.js:1 Error: Request failed with status code 403
I'm new to implementing auth so this is all a bit confusing, so how do I make '/registergig' a protected page that allows users to submit information?
Here's the Login component:
import React from 'react'
import Header from './Header'
import Button from '#material-ui/core/Button'
import axios from 'axios'
import {Link} from 'react-router-dom'
class Login extends React.Component {
constructor() {
super();
this.state = {
email: "",
password: "",
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
handleSubmit(e) {
console.log("submit reached");
e.preventDefault();
const loginData = {
email: this.state.email,
password: this.state.password,
};
axios("http://localhost:5000/gig-fort/us-central1/api/login", {
method: "POST",
headers: {
"content-type": "application/json",
},
data: loginData,
})
.then((res) => {
console.log(res.data);
this.props.history.push("/gigregister");
})
.catch((err) => {
console.error(err);
});
}
render() {
return (
<>
<div>
<Header />
</div>
<Link to="/Homepage" style={{ textDecoration: "none" }}>
<h1 className="login-header">Gigs this week</h1>
</Link>
<div className="login-main">
<div className="login">
<h2>Venue login</h2>
<form onSubmit={this.handleSubmit}>
<input
type="text"
name="email"
placeholder="email"
onChange={this.handleChange}
/>
<br></br>
<input
type="password"
name="password"
placeholder="password"
onChange={this.handleChange}
/>
<div className="button">
<Button type="submit">Submit</Button>
</div>
</form>
</div>
<Link to="/venueregister" style={{ textDecoration: "none" }}>
<h2 style={{ color: "#b49650" }}>Register a venue</h2>
</Link>
</div>
</>
);
}
}
export default Login;
Here's the GigRegister component, that takes in user information:
import React from "react";
import Header from "./Header";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import axios from "axios";
class GigRegister extends React.Component {
constructor() {
super();
this.state = {
name: "",
venue: "",
time: "",
date: "",
genre: "",
tickets: "",
price: "",
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
handleSubmit(e) {
console.log("submit function reached");
e.preventDefault();
const gigData = {
name: this.state.name,
venue: this.state.venue,
time: this.state.time,
date: this.state.date,
genre: this.state.genre,
tickets: this.state.tickets,
price: this.state.price,
};
axios("http://localhost:5000/gig-fort/us-central1/api/createGigListing", {
method: "POST",
headers: {
"content-type": "application/json",
},
data: gigData,
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.error(err);
});
}
setDate() {}
render() {
return (
<div className="gig-register">
<Header />
<h1 className="header-gigReg">Register a gig</h1>
<form onSubmit={this.handleSubmit}>
<TextField
placeholder="Event name"
defaultValue="Event name"
id="name"
name="name"
onChange={this.handleChange}
/>
<TextField
placeholder="Venue"
defaultValue="Venue"
id="venue"
name="venue"
onChange={this.handleChange}
/>
<TextField
placeholder="Time"
defaultValue="Time"
type="time"
label="Enter start time"
id="time"
name="time"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
onChange={this.handleChange}
/>
<TextField
id="date"
label="Select date"
type="date"
defaultValue="2017-05-24"
InputLabelProps={{
shrink: true,
}}
onChange={(e) => {
this.setState({ date: e.target.value });
}}
/>
<TextField
placeholder="Genre"
defaultValue="Genre"
id="genre"
name="genre"
onChange={this.handleChange}
/>
<TextField
placeholder="Tickets"
defaultValue="Tickets"
id="tickets"
name="tickets"
onChange={this.handleChange}
/>
<TextField
placeholder="Price"
defaultValue="Price"
id="price"
name="price"
onChange={this.handleChange}
/>
<Button type="submit">Submit</Button>
</form>
</div>
);
}
}
export default GigRegister
...and here's the express/firebase functions:
const FBAuth = (req, res, next) => {
let idToken;
if(req.headers.authorization && req.headers.authorization.startsWith('Bearer ')){
idToken = req.headers.authorization.split('Bearer ')[1]
} else {
console.error('No token found')
return res.status(403).json({error: 'Unauthorized'})
}
admin.auth().verifyIdToken(idToken)
.then(decodedToken => {
req.user = decodedToken;
return db.collection('users')
.where('userId', '==',req.user.uid)
.limit(1)
.get()
})
.then(data =>{
req.user.venueName = data.docs[0].data().venueName;
return next();
})
.catch(err => {
console.error('Error while verifying token', err)
return res.status(403).json(err)
})
}
app.post('/createGigListing', FBAuth, (req,res) => {
const newGig = {
venueName: req.user.venueName,
name: req.body.name,
time: req.body.time,
price: req.body.price,
genre: req.body.genre,
tickets: req.body.tickets,
date: req.body.date
}
db
.collection('gig-listing')
.add(newGig)
.then(doc => {
res.json({message: `document ${doc.id} created successfully`})
})
.catch(err =>{
res.status(500).json({error: 'something went wrong'})
console.error(err)
})
})
Your Cloud Functions code expects a Authorization header with a Bearer token in there, but your client-side code is not passing that in this call:
axios("http://localhost:5000/gig-fort/us-central1/api/createGigListing", {
method: "POST",
headers: {
"content-type": "application/json",
},
data: gigData,
})
So you'll have to add a header here, passing in the ID token:
firebase.auth().currentUser.getIdToken().then(function(token) {
axios("http://localhost:5000/gig-fort/us-central1/api/createGigListing", {
method: "POST",
headers: {
"content-type": "application/json",
"Authorization": "Bearer "+token,
},
data: gigData,
})
});
I loosely based this on this code from the functions-samples repo.

Google ReCaptcha: react-google-recaptcha not verifying correctly

I have the following React component for a contact form:
import React from 'react'
import ReCAPTCHA from "react-google-recaptcha";
import {Container, Row, Col, Form, Button } from 'react-bootstrap'
import '../styles/contact.css'
class Contact extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: '',
email: '',
company: '',
content: '',
showSuccess: false,
submitting: false,
verified: false,
reply: ''
};
this.handleSuccess = this.handleSuccess.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.onChange = this.onChange.bind(this);
}
onChange = (value) => {
console.log("Captcha value:", value);
this.setState({
verified: true
})
};
handleInputChange = event => {
const target = event.target
const value = target.value
const name = target.name
this.setState({
[name]: value,
})
}
handleSuccess = () => {
this.setState({
name: '',
email: '',
company: '',
content: '',
showSuccess: true,
submitting: false,
})
}
handleSubmit = event => {
const url = 'https://xxxxxxxx.execute-api.eu-central-1.amazonaws.com/dev/email/send';
this.setState({
submitting: true
})
const payload = {
name: this.state.name,
email: this.state.email,
company: this.state.company,
content: this.state.content
}
if (this.state.verified) {
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
.then(this.handleSuccess)
.catch(error => alert(error))
event.preventDefault()
}
else {this.setState({reply: "Please verify the ReCaptcha."})}
}
render() {
return (
<section id="contact" name="#contact">
<Container>
<Row className="align-items-center">
<Col lg={{span: 5, order: 1}} xs={{ span: 12, order: 2}} className="form-background">
<Form id="contact-form-bottom" onSubmit={this.handleSubmit}>
<h4 className="h4">Don't be shy, say hi!</h4>
<Form.Group controlId="formBasicEmail">
<Form.Label>Full Name</Form.Label>
<Form.Control
as="input"
type="text"
placeholder="Enter your first name & surname"
name="name"
value={this.state.name}
onChange={this.handleInputChange}
required
/>
</Form.Group>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control
as="input"
type="email"
placeholder="Enter your email address"
name="email"
value={this.state.email}
onChange={this.handleInputChange}
required
/>
<Form.Text className="text-muted">
We'll never share your email with anyone else.
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicEmail">
<Form.Label>Company Name</Form.Label>
<Form.Control
as="input"
type="text"
placeholder="Enter the name of your company"
name="company"
value={this.state.company}
onChange={this.handleInputChange}
required
/>
</Form.Group>
<Form.Group controlId="exampleForm.ControlTextarea1">
<Form.Label>Details</Form.Label>
<Form.Control
as="textarea"
type="text"
rows="3"
placeholder="How can we help you?"
name="content"
value={this.state.content}
onChange={this.handleInputChange}
required
/>
</Form.Group>
<ReCAPTCHA
className="g-recaptcha"
sitekey="XXXXXXXXXXXXXXXXXXX"
onChange={this.onChange}
theme="dark"
/>
{ this.state.verified ? <p id="error" className="error">{this.state.reply}</p> : null }
{ this.state.showSuccess ? <p id="success" className="success">Thank you, we will be in touch asap.</p> : null }
<Button id="submit" variant="primary" type="submit">
Submit
</Button>
</Form>
</Col>
</Row>
</Container>
</section>
)
}
}
export default Contact
Desired behaviour
I'm using react-google-recaptcha (https://www.npmjs.com/package/react-google-recaptcha) for Recaptcha verification and inserted the component top of the submit button:
<ReCAPTCHA
className="g-recaptcha"
sitekey="XXXXXXXXXXXXXXXXXXXXX"
onChange={this.onChange}
theme="dark"
/>
The onChange function should set state.verfied == true
onChange = (value) => {
console.log("Captcha value:", value);
this.setState({
verified: true
})
};
so that this part of handledSubmit() fires if the reCaptcha was completed and the form gets submitted without page reload:
if (this.state.verified) {
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
.then(this.handleSuccess)
.catch(error => alert(error))
event.preventDefault()
}
otherwise it should render this.state.reply: "Please verify the ReCaptcha." above the submit button.
What it does instead
The submit button works without completing the reCaptcha and submits the form. It reloads the page to http://localhost:8000/?name=Test&email=test&company=test&content=test&g-recaptcha-response=
I know that my workaround using state probably isn't the correct way of using this module, but the react-google-recaptcha docs leave no hint on how to correctly integrate the verification with the reCaptcha API.
Happy for any support on this. Thanks!
I needed to change the onChange function to store the reCaptchaResponse like so:
onChange = (result) => {
this.setState({
verified: true,
reCaptchaResponse: result,
})
};
And secondly I updated the condition in handleSubmit to check for verification much earlier and also integrate the reCaptchaResponse in the payload like so:
handleSubmit = event => {
if (this.state.verified) {
const url = 'https://xxxxxxxxxx.execute-api.eu-central-1.amazonaws.com/dev/email/send';
this.setState({
submitting: true
})
const payload = {
name: this.state.name,
email: this.state.email,
company: this.state.company,
content: this.state.content,
result: this.state.reCaptchaResponse
}
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
.then(this.handleSuccess)
.catch(error => alert(error))
event.preventDefault()
} else {
this.setState({
reply: "Please verify the ReCaptcha"
})
}
}

Resources