Event undefined for event.preventDefault in a React form - javascript

I'm trying to create a user authentication page using React and Apollo. I'm getting that my event is undefined for onClick event for the following code:
const Auth = props => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const onSubmitHandler = async (login, e) => {
e.preventDefault()
const authResults = await login()
props.history.push('/')
}
return (
<Mutation
mutation={LOGIN}
variables={{ email, password }}
>
{(login, {data, error, loading}) => {
if(loading) return <div>...loading</div>
if(error) return <div>Error</div>
return (
<form onSubmit={onSubmitHandler}>
<fieldset>
<label htmlFor="email">
Email
</label>
<input
type="email"
id="email"
value={email}
onChange={e => {
setEmail(e.target.value)
}}
/>
</fieldset>
<fieldset>
<label htmlFor="password">
Password
</label>
<input
type="password"
id="password"
value={password}
onChange={e => {
setPassword(e.target.value)
}}
/>
</fieldset>
<button>Login</button>
</form>
)
}}
</Mutation>
)
I tried including the variables at the point of invocation as following:
<form onSubmit={(login, e) => onSubmitHandler(login, e)}>
But, to no avail.
Edit: The initial issue has been resolved, but when I refactored the code to utilize react-apollo-hooks, I keep getting the following errors:
Variable "$email" of required type "String!" was not provided
Variable "$password" of required type "String!" was not provided
My refactored codes are as follows:
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const variables = {
data: { email, password }
}
const login = useMutation(LOGIN_MUTATION, {
variables
})
const onSubmitHandler = async (login, e) => {
e.preventDefault()
const authResults = await login()
localStorage.setItem(AUTH_TOKEN, authResults.data.login.token);
props.history.push('/')
}
The graphql mutation
const LOGIN_MUTATION = gql`
mutation Login($email: String!, $password: String!) {
login(data: {
email: $email, password: $password
}
){
token
user {
id
}
}
}
`
My schema
login(data: LoginUserInput!): AuthPayload!

Related

Can You Add Data to Database (supabase) without an Server using ReactJS

I'm kinda new in programming and I'm learning how the web works, And Im working on this project using Supabase PostgreeSQL with Api, so what I want to do is write (send data to db), and I want to do it using only ReactJS but I don't know if this is possible, I have made something to work only with JS and the API keyes from SUPABASE, but I don't know if this is correct to do
import {useState} from 'react'
import {supabase} from '../db/supabase';
import Helmet from "helmet";
import {Link} from 'react-router-dom'
import styled from "styled-components";
const Container = styled.div``
const LeftContainer = styled.div``
const RightContainer = styled.div``
const Input = styled.input``
const BtnButBlack = styled.button``
const BtnButGreen = styled.button``
const Form = styled.form``
export default function Signup() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [name, setName] = useState('');
const [username, setUsername] = useState('');
const handleSubmit = (event) => {
new_account({email: email, password: password}, name, username)
event.preventDefault();
};
const handleEmail = (event) => {
event.preventDefault();
setEmail(event.target.value)
};
const handlePassword = (event) => {
event.preventDefault();
setPassword(event.target.value)
}
const handleName = (event) => {
event.preventDefault();
setName(event.target.value)
}
const handleUsername = (event) => {
event.preventDefault();
setUsername(event.target.value)
}
const new_account = async ({email, password}) => {
const {user, session, error} = await supabase.auth.signUp({
email: email,
password: password,
})
if (error) {
console.error(error)
return
}
console.log(user);
await supabase
.from('Users')
.insert([
{username: username, name: name, email: email, password: password}
])
};
return (
<Container>
<LeftContainer>
Signup
</LeftContainer>
<RightContainer>
<Form onSubmit={handleSubmit}>
<Input onChange={handleUsername} type="text" value={username} placeholder="Username" autoComplete="false" /><br></br>
<Input onChange={handleName} type="text" value={name} placeholder="Name" autoComplete="false" /><br></br>
<Input onChange={handleEmail} type="text" value={email} placeholder="Email" autoComplete="false" /><br></br>
<Input onChange={handlePassword} type="password" value={password} placeholder="Password" autoComplete="false" /><br />
<BtnButGreen>Sign Up</BtnButGreen>
</Form>
</RightContainer>
</Container>
)
}
and this is the supabase module that you can import and use it on the jsx file:
import {createClient} from '#supabase/supabase-js'
const supabaseUrl = process.env.REACT_APP_SUPABASE_URL
const supabaseAnonKey = process.env.REACT_APP_SUPABASE_ANON_KEY
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
The code you have seems great! With Supabase, the you are only exposing anon key and supabase URL, which are meant to be public.
You use row level security and Supabase Auth to prevent malicious attackers to hack your database. You can read more about row level security here:
https://supabase.io/docs/learn/auth-deep-dive/auth-row-level-security
If you have any questions, please don't hesitate to ask!

React encrypt password

I have following component to pass data in my firestore database. Currently the password is not encrypted and and is visible to users of the database. Therefore I want to encrypt it. However, I get the error
TypeError: Cannot read property 'encrypt' of undefined.
That is my component for putting data in the database:
import React, {useState} from "react";
import fire from './fire.js';
import {userAuth} from './Main';
import '../style/inputPasswords.css';
import {encrypt} from './encryption';
const database = fire.firestore();
const InputPasswords = () => {
const [title, setTitle] = useState("");
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
let encryptedPassword = encrypt(password);
database.collection("password-"+userAuth).add({
title: title,
username: username,
password: encryptedPassword
})
.then(() => {
window.location.reload();
})
.catch((error) => {
console.error(error);
})
setTitle("");
setUsername("");
setPassword("");
}
return (
<form className="form" onSubmit={handleSubmit}>
<label>title</label>
<input className="input" id="title" placeholder="Title" value={title} autoComplete="off"
onChange={(e) => setTitle(e.target.value)}/>
<label>Username</label>
<input className="input" id="username" placeholder="Username" value={username} autoComplete="off"
onChange={(e) => setUsername(e.target.value)}/>
<label>Password</label>
<input className="input" id="password" placeholder="Password" type="password" value={password} autoComplete="off"
onChange={(e) => setPassword(e.target.value)}/>
<button type="submit">Add</button>
</form>
)
}
export default InputPasswords
This is the code for the encryption:
import crypto from "crypto";
const secret = "testtesttesttesttesttesttesttest";
const encrypt = (password) => {
return crypto.AES.encrypt(password, secret).toString();
};
const decrypt = (encryption) => {
let bytes = crypto.AES.decrypt(encryption, secret);
let originalText = bytes.toString(crypto.enc.Utf8);
return originalText;
};
export {encrypt, decrypt};
I am not sure how to fix that. Does anyone have an idea how to solve that problem?
Because without the encryption the code runs without any problems
I edited and moved the encryption function into the component and so the password gets encrypted
const handleSubmit = (e) => {
e.preventDefault();
const secret = "testtesttesttesttesttesttesttest";
const iv = Buffer.from(crypto.randomBytes(16));
const cipher = crypto.createCipheriv('aes-256-ctr', Buffer.from(secret), iv);
const encryptedPassword = Buffer.concat([cipher.update(password), cipher.final()]);
//let encryptedPassword = crypto.AES.encrypt("password", secret).toString();;
database.collection("password-"+userAuth).add({
title: title,
username: username,
password: encryptedPassword.toString('hex')
})
.then(() => {
window.location.reload();
})
.catch((error) => {
console.error(error);
})
setTitle("");
setUsername("");
setPassword("");
}

When should I call custom hook not breaking any rules of hooks?

I do have a simple component with form. I want to use useSendEmail hook which returns the server response (success or failure). Where do I call this hook so it doesn't fire on the first render but only after I get my data from the user and save it in the state?
Expected behaviour: hook useSendEmail is called when the email object contains users input and returns status (if sent successfully or not).
I am aware I need to call hook at the top level in the component, but I do I wait for the data from input fields?
Actual behaviour: I am breaking the rule of hooks.
// importing everything I need here
const ContactPage = () => {
const initial = {
from: '',
message: '',
email: '',
};
const [formData, setFormData] = useState(initial);
const [email, setEmail] = useState(null);
const handleChange = ({ name, value }) => {
setFormData({ ...formData, [name]: value });
};
useEffect(() => {
if (email === null) return;
const response = useSendEmail(email);
}, [email]);
const handleSubmit = (e) => {
e.preventDefault();
setEmail(formData);
};
return (
<DefaultLayout title="Contact">
<StyledContainer>
<form className="contact_form" onSubmit={(e) => handleSubmit(e)}>
<input
name="from"
type="text"
value={formData.from}
onChange={(e) => handleChange(e.target)}
placeholder="Your full name"
/>
<textarea
name="message"
value={formData.message}
onChange={(e) => handleChange(e.target)}
placeholder="Your Message"
/>
<input
name="email"
type="email"
value={formData.email}
onChange={(e) => handleChange(e.target)}
placeholder="Your e-mail"
/>
<button type="submit">SUBMIT</button>
</form>
</StyledContainer>
</DefaultLayout>
);
};
export default ContactPage;
EDIT:
this is how my hook looks like after refactoring with your suggestions. I am now importing the hook and the method in the top level component and everything seems to work perfectly.
import { useState } from 'react';
import emailjs from 'emailjs-com';
import { userID, templateID, serviceID } from '../data/account';
const useSendEmail = (email) => {
const [response, setResponse] = useState(null);
const successMsg = 'Your message has been successfully sent';
const errorMsg = 'Your message has not been sent. Try again.';
const sendEmail = async () => emailjs
.send(serviceID, templateID, email, userID)
.then(
(res) => {
if (res.status === 200) {
setResponse(successMsg);
}
if (res.status !== 200) {
setResponse(errorMsg);
}
},
(err) => {
console.log(err);
},
);
return { response, sendEmail }
};
export default useSendEmail;

How do i get the value of text input field using react

I'm creating a register form in react with validation. The values i'm asking is Username, Email, password + (controll password). Everything about the form works, validations, Errors and if you click sign up you go to a new page. Now i want to extract the values to a my MySql database. I succeed in putting stuff in my database so the link works but i can't get the values of what i typed in the form.
I have tried
onChange={(e) => {
setUsernameReg(e.target.value);
}}
(see commented item)
But when i tried this I couldn't fill anything in Username. The code for the other inputs (email, password) is the same apart from the names.
So in short I want to get the value what you typed in a textbox to my database.
Code: FormSignup.js
import React, { useEffect, useState } from 'react';
import Axios from 'axios';
import validate from './validateInfo';
import useForm from './useForm';
import './Form.css';
const FormSignup = ({ submitForm }) => {
const { handleChange, handleSubmit, values, errors } = useForm(
submitForm,
validate
);
const [usernameReg, setUsernameReg] = useState("");
const [emailReg, setEmailReg] = useState("");
const [passwordReg, setPasswordReg] = useState("");
Axios.defaults.withCredentials = true;
const register = () => {
Axios.post("http://localhost:3001/register", {
username: usernameReg,
password: passwordReg,
email: emailReg,
}).then((response) => {
console.log(response);
});
};
return (
<div className='form-content-right'>
<form onSubmit={handleSubmit} className='form' noValidate>
<h1>
Get started with us today! Create your account by filling out the
information below.
</h1>
<div className='form-inputs'>
<label className='form-label'>Username</label>
<input
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
value={values.username}
onChange={handleChange}
/*
//onChange={(e) => {
// setUsernameReg(e.target.value);
//}}
*/
/>
Code UseForm.js
import { useState, useEffect } from 'react';
import Axios from 'axios';
const useForm = (callback, validate) => {
const [values, setValues] = useState({
username: '',
email: '',
password: '',
password2: ''
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [usernameReg, setUsernameReg] = useState("");
const [emailReg, setEmailReg] = useState("");
const [passwordReg, setPasswordReg] = useState("");
Axios.defaults.withCredentials = true;
const register = () => {
Axios.post("http://localhost:3001/register", {
username: usernameReg,
password: passwordReg,
email: emailReg,
}).then((response) => {
console.log(response);
});
};
const handleChange = e => {
const { name, value } = e.target;
setValues({
...values,
[name]: value
});
};
const handleSubmit = e => {
e.preventDefault();
setErrors(validate(values));
setIsSubmitting(true);
};
useEffect(
() => {
if (Object.keys(errors).length === 0 && isSubmitting) {
callback();
}
},
[errors]
);
return { handleChange, handleSubmit, values, errors };
};
export default useForm;
The code is from https://www.youtube.com/watch?v=KGFG-yQD7Dw&t and https://www.youtube.com/watch?v=W-sZo6Gtx_E&t
By using the value prop of the input, you turn it into a controled input element and thus need to update its value via a state variable. So this:
<input
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
value={values.username}
onChange={handleChange}
/*
//onChange={(e) => {
// setUsernameReg(e.target.value);
//}}
*/
/>
Should just be this:
<input
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
value={usernameReg}
onChange={e => setUsernameReg(e.target.value)}
/>
Note that this only answers this part:
I succeed in putting stuff in my database so the link works but i can't get the values of what i typed in the form
So this is how you can access those values. I can't guide you on how to get those values all the way to your DB as there is a longer distance they have to travel and I don't know what else could be in the way.
You should also look into useRef(), which will give you access to those input fields without updating your state on every change of the input and thus re-rendering your form over and over.
You can do something like this:
...
const regInput = React.useRef();
...
...
<input
ref={regInput}
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
Then when you're ready to submit, just access the value of the username input like so:
...
const v = regInput.current.value;
...

The code in the then method is not executed and error handling is performed

I'm developing a web app in React and firebase and I'm having trouble getting it to work.
Here is my code
import React, { useRef, useState } from "react"
import { Form, Button, Card, Alert } from "react-bootstrap"
import { useAuth } from "../contexts/AuthContext"
import { Link, useHistory, Redirect, Route } from "react-router-dom"
import { db } from "../firebase"
import Dashboard from "../components/Dashboard"
export default function UpdateProfile() {
const usernameRef = useRef()
const emailRef = useRef()
const passwordRef = useRef()
const passwordConfirmRef = useRef()
const { updateUser, currentUser, updatePassword } = useAuth()
const [error, setError] = useState("")
const [loading, setLoading] = useState(false)
const history = useHistory()
function handleSubmit(e) {
e.preventDefault()
if (passwordRef.current.value !== passwordConfirmRef.current.value) {
return setError("Passwords do not match")
}
if (passwordRef.current.value) {
updatePassword(passwordRef.current.value)
}
const uid = currentUser.uid
db.collection('users').doc(uid).get()
.then(snapshot => {
const data = snapshot.data()
try {
setLoading(true)
setError("")
updateUser(usernameRef.current.value, emailRef.current.value, data)
history.push('/dashboard')
} catch {
setError("Failed to update account")
}
setLoading(false)
})
}
return (
<>
<Card>
<Card.Body>
<h2 className="text-center mb-4">Update Profile</h2>
{error && <Alert variant="danger">{error}</Alert>}
<Form onSubmit={handleSubmit}>
<Form.Group id="username">
<Form.Label>Name</Form.Label>
<Form.Control
type="text"
ref={usernameRef}
required
defaultValue={currentUser.username}
/>
</Form.Group>
<Form.Group id="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
ref={emailRef}
required
defaultValue={currentUser.email}
/>
</Form.Group>
<Form.Group id="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
ref={passwordRef}
placeholder="Leave blank to keep the same"
/>
</Form.Group>
<Form.Group id="password-confirm">
<Form.Label>Password Confirmation</Form.Label>
<Form.Control
type="password"
ref={passwordConfirmRef}
placeholder="Leave blank to keep the same"
/>
</Form.Group>
<Button disabled={loading} className="w-100" type="submit">
Update
</Button>
</Form>
</Card.Body>
</Card>
<div className="w-100 text-center mt-2">
<Link to="/">Cancel</Link>
</div>
</>
)
}
This is the code for the user's edit function: enter a value in the form and press the button to run handleSubmit.
function updateUser(username, email, data) {
const uid = data.uid
db.collection('users').doc(uid).set({
email: email,
username: username,
}, {merge: true})
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async(user) => {
if (user) {
const uid = user.uid
console.log(uid)
await db.collection('users').doc(uid).get()
.then(snapshot => {
const data = snapshot.data()
setCurrentUser(data)
setLoading(false)
})
}
})
return unsubscribe
}, [])
And the following code rewrites the firestore data After this updateUser function is executed, we want to do a history.push in then of handleSubmit to redirect to /dashboard, but we want to get the console to say "success! in the console and the "failure! in the console and the message "success!" appears on the app.
When I looked at the firestore data, I found that the values I entered were properly reflected in the firestore.
This tells me that the then part of handleSubmit is not working, but I don't know why it's not working.
If you have a solution, I'd love to hear it.
Thank you.
You need to
Either await or put a .catch onto the updateUser Promise chain (the try/catch around it will only catch async errors if the Promise is awaited)
Return the Promise from updateUser
Pass a function to the .then callback - your .then(console.log("success!!")) invokes console.log immediately and passes undefined to the .then
function handleSubmit(e) {
e.preventDefault()
if (passwordRef.current.value !== passwordConfirmRef.current.value) {
return setError("Passwords do not match")
}
if (passwordRef.current.value) {
updatePassword(passwordRef.current.value)
}
const uid = currentUser.uid
db.collection('users').doc(uid).get()
.then(snapshot => {
const data = snapshot.data()
setLoading(true)
setError("")
return updateUser(usernameRef.current.value, emailRef.current.value, data);
})
.then(() => {
// Success
history.push('/dashboard')
})
.catch((error) => {
setError("failed!!")
})
.finally(() => {
setLoading(false)
});
}
function updateUser(username, email, data) {
const uid = data.uid
return db.collection('users').doc(uid).set({
email: email,
username: username,
}, { merge: true })
.then(() => console.log("success!!"))
}

Categories

Resources