hello guys pls i have a challenge in my react app, i am trying to enable multiple choice login where a user can either use his mail or even hi username to login. i got it working from my server using postman but when i try it on my frontend it doesnt allow me to use multiple refs on it, i am only allowed to use either the username or email.
Here is my code
function Login() {
const password = useRef();
const email = useRef();
const username = useRef();
const { isFetching, dispatch } = useContext(AuthContext);
const handleClick = (e) => {
e.preventDefault();
try {
loginCall(
{
username: username.current.value,
email: email.current.value,
password: password.current.value,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
return (
<>
<div className="log__bg">
<div className="login">
<div className="loginWrapper">
<div className="loginLeft">
<span className="loginDesc">Welcome Back</span>
<ToastContainer />
</div>
<div className="loginRight">
<form className="loginBox" onSubmit={handleClick} >
<input
placeholder="Email, phone, or username"
type="text"
required
className="loginInput"
ref={username}
/>
<input
placeholder="Password"
type="password"
required
minLength="6"
className="loginInput"
ref={password}
/>
<button
className="loginButton"
type="submit"
disabled={isFetching}
>
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Log In"
)}
</button>
<Link id="auth_route" to="/register">
<button className="loginRegButton">
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Create a New Account"
)}
</button>
</Link>
<div className="forgot__password">
<Link className="forgot__password__btn" to="/forgot-password">
Forgot Password?
</Link>
</div>
</form>
</div>
</div>
</div>
</div>
</>
);
}
export default Login;
Why you need 2 refs ? user will provide username or email in the same input field. Use just one ref than you can have a logic or regex in your backend to detect if the provided value is an email or a username:
const handleClick = (e) => {
e.preventDefault();
let username = null
let email = null
const emailRegex = /^\S+#\S+\.\S+$/
const inputValue = username.current.value
if(inputValue.match(emailRegex)){
email = inputValue
} else {
username = inputValue
}
try {
loginCall(
{
username: username,
email: email,
password: password.current.value,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
With this if the user provided an email, username will be sent as null to the server and vice versa. Then you can check on the server if you get an email or a username.
UPDATE:
You can also just use a state and don't use ref at all:
const [usernameValue, setUsernameValue]=React.useState("")
const [passwordValue, setPasswordValue]=React.useState("")
const handleClick = (e) => {
e.preventDefault();
let username = null
let email = null
const emailRegex = /^\S+#\S+\.\S+$/
if(usernameValue.match(emailRegex)){
email = inputValue
} else {
username = inputValue
}
try {
loginCall(
{
username: username,
email: email,
password: passwordValue,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
return (
...
<form className="loginBox" onSubmit={handleClick} >
<input
placeholder="Email, phone, or username"
type="text"
required
className="loginInput"
value={usernameValue}
onChange={e=>setUsernameValue(e.target.value)}
/>
<input
placeholder="Password"
type="password"
required
minLength="6"
className="loginInput"
value={passwordValue}
onChange={e=>setPasswordValue(e.target.value)}
/>
...
...
)
I agree with #PatersonCode, the best way would be to use React.useState and just one variable to store your input value.
You didn't mention what error you're getting, but here's an example that I hope might help you in some way:
https://codesandbox.io/s/choose-login-method-example-xgpfue?file=/src/Login.js
thanks to everyone that helped, i have solved it and the problem was that i needed to use one ref (email or username) and also that same ref in my loginCall. here is the updated code.
function Login() {
const password = useRef();
const username = useRef();
const { isFetching, dispatch } = useContext(AuthContext);
const handleClick = (e) => {
e.preventDefault();
try {
loginCall(
{
username: username.current.value,
email: username.current.value,
password: password.current.value,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
return (
<>
<div className="log__bg">
<div className="login">
<div className="loginWrapper">
<div className="loginLeft">
<span className="loginDesc">Welcome Back</span>
<ToastContainer />
</div>
<div className="loginRight">
<form className="loginBox" onSubmit={handleClick} >
<input
placeholder="Email, phone, or username"
type="text"
required
className="loginInput"
ref={username}
/>
<input
placeholder="Password"
type="password"
required
minLength="6"
className="loginInput"
ref={password}
/>
<button
className="loginButton"
type="submit"
disabled={isFetching}
>
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Log In"
)}
</button>
<Link id="auth_route" to="/register">
<button className="loginRegButton">
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Create a New Account"
)}
</button>
</Link>
<div className="forgot__password">
<Link className="forgot__password__btn" to="/forgot-password">
Forgot Password?
</Link>
</div>
</form>
</div>
</div>
</div>
</div>
</>
);
}
export default Login;
Related
How can I solve the Axios Error in my code using the try/catch method . Am building a chat application with react js and stream API, when I try to signup using my signup form I get the Axios error which I don't know how I can debug it. You can help me out by editing my attached code so that i can continue with my project. Thanks in advance.
// below is my code//
import React, { useState } from 'react';
import Cookies from 'universal-cookie';
import axios from 'axios';
import signinImage from '../assets/signup.jpg';
const cookies = new Cookies();
const initialState = {
fullName: '',
username: '',
password: '',
confirmPassword: '',
phoneNumber: '',
avatarURL: '',
}
const Auth = () => {
const [form, setForm] = useState(initialState);
const [isSignup, setIsSignup] = useState(true);
const handleChange = (e) => {
setForm({ ...form, [e.target.name]: e.target.value });
}
const handleSubmit = async (e) => {
e.preventDefault();
const { username, password, phoneNumber, avatarURL } = form;
const URL = 'https://localhost:5000/auth';
// const URL = 'https://medical-pager.herokuapp.com/auth';
const { data: { token, userId, hashedPassword, fullName } } = await axios.post(`${URL}/${isSignup ? 'signup' : 'login'}`, {
username, password, fullName: form.fullName, phoneNumber, avatarURL,
});
cookies.set('token', token);
cookies.set('username', username);
cookies.set('fullName', fullName);
cookies.set('userId', userId);
if(isSignup) {
cookies.set('phoneNumber', phoneNumber);
cookies.set('avatarURL', avatarURL);
cookies.set('hashedPassword', hashedPassword);
}
window.location.reload();
}
const switchMode = () => {
setIsSignup((prevIsSignup) => !prevIsSignup);
}
return (
<div className="auth__form-container">
<div className="auth__form-container_fields">
<div className="auth__form-container_fields-content">
<p>{isSignup ? 'Sign Up' : 'Sign In'}</p>
<form onSubmit={handleSubmit}>
{isSignup && (
<div className="auth__form-container_fields-content_input">
<label htmlFor="fullName">Full Name</label>
<input
name="fullName"
type="text"
placeholder="Full Name"
onChange={handleChange}
required
/>
</div>
)}
<div className="auth__form-container_fields-content_input">
<label htmlFor="username">Username</label>
<input
name="username"
type="text"
placeholder="Username"
onChange={handleChange}
required
/>
</div>
{isSignup && (
<div className="auth__form-container_fields-content_input">
<label htmlFor="phoneNumber">Phone Number</label>
<input
name="phoneNumber"
type="text"
placeholder="Phone Number"
onChange={handleChange}
required
/>
</div>
)}
{isSignup && (
<div className="auth__form-container_fields-content_input">
<label htmlFor="avatarURL">Avatar URL</label>
<input
name="avatarURL"
type="text"
placeholder="Avatar URL"
onChange={handleChange}
required
/>
</div>
)}
<div className="auth__form-container_fields-content_input">
<label htmlFor="password">Password</label>
<input
name="password"
type="password"
placeholder="Password"
onChange={handleChange}
required
/>
</div>
{isSignup && (
<div className="auth__form-container_fields-content_input">
<label htmlFor="confirmPassword">Confirm Password</label>
<input
name="confirmPassword"
type="password"
placeholder="Confirm Password"
onChange={handleChange}
required
/>
</div>
)}
<div className="auth__form-container_fields-content_button">
<button>{isSignup ? "Sign Up" : "Sign In"}</button>
</div>
</form>
<div className="auth__form-container_fields-account">
<p>
{isSignup
? "Already have an account?"
: "Don't have an account?"
}
<span onClick={switchMode}>
{isSignup ? 'Sign In' : 'Sign Up'}
</span>
</p>
</div>
</div>
</div>
<div className="auth__form-container_image">
<img src={signinImage} alt="sign in" />
</div>
</div>
)
}
export default Auth
const handleSubmit = async (e) => {
e.preventDefault();
try {
const { username, password, phoneNumber, avatarURL } = form;
const URL = 'https://localhost:5000/auth';
// const URL = 'https://medical-pager.herokuapp.com/auth';
const { data: { token, userId, hashedPassword, fullName } } = await axios.post(`${URL}/${isSignup ? 'signup' : 'login'}`, {
username, password, fullName: form.fullName, phoneNumber, avatarURL,
});
cookies.set('token', token);
cookies.set('username', username);
cookies.set('fullName', fullName);
cookies.set('userId', userId);
if(isSignup) {
cookies.set('phoneNumber', phoneNumber);
cookies.set('avatarURL', avatarURL);
cookies.set('hashedPassword', hashedPassword);
}
window.location.reload();
} catch(error) {
console.log(error.response);
}
}
This would be my code, I am making a small application with an internal database and a registration form. I would like that, when the user registers with a different username than the ones I have created in my database and clicks on register, he/she will be redirected to my Planets component.
This my the error url: http://localhost:3000/Registration/Planets
This should be: http://localhost:3000/Planets
This is my code in the Registration component:
import { Link } from 'react-router-dom';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import swal from 'sweetalert';
import yoda from '../images/yoda.png';
function Registration() {
//error
const [errorMessages, setErrorMessages] = useState({});
const [isSubmitted, setIsSubmitted] = useState(false);
const navigate = useNavigate();
//error
const renderErrorMessage = (name) =>
name === errorMessages.name && (
<div className="error">{errorMessages.message}</div>
);
// User Login info
const database = [
{
username: 'Luke',
},
{
username: 'Leia',
},
];
const errors = {
uname: 'This user already exists',
};
const alert = () => {
swal({
title: 'Confirming registration',
icon: 'success',
button: 'Ok',
timer: '2000',
});
};
function handleSubmit(event) {
//Prevent page reload
event.preventDefault();
var { uname } = document.forms[0];
// Find user login info
const userData = database.find((user) => user.username === uname.value);
// Compare user info
if (userData) {
if (userData.username === uname.value) {
// Invalid password
setErrorMessages({ name: 'uname', message: errors.uname });
}
} else {
// Username not found
setIsSubmitted(true);
navigate('Planets');
alert();
}
}
return (
<>
<div className="main">
<section className="main__section">
<form className="main__section__form" onSubmit={handleSubmit}>
<h3 className="main__section__form__title">CREATE YOUR ACCOUNT</h3>
<div className="input-container">
<input type="text" name="uname" placeholder="Username" required />
{renderErrorMessage('uname')}
</div>
<div className="input-container">
<input
type="password"
name="pass"
placeholder="Password"
required
/>
</div>
<div className="input-container">
<input
type="text"
name="pass"
placeholder="First Name"
required
/>
</div>
<div className="input-container">
<input type="text" name="pass" placeholder="Last Name" required />
</div>
<div className="button-container">
<button className="button">Register</button>
</div>
<Link to="/">
<p className="main__section__form__p">
Already have an account?{' '}
<span className="main__section__form__span">Back to Login</span>
</p>
</Link>
</form>
</section>
<div>
<img className="main__imageYoda" src={yoda} alt="Yoda" />
</div>
</div>
</>
);
}
You actually need to replace the previous path and not push a new one into the history.
Try to use navigate("/Planets", { replace: true }).
Also keep in mind that routes should start with a lower case letter (/registration, /planets)
I have developed a simple login component for my project. In this I am having two fields (UserName and Password) along with onSubmit handler. I need to pass the onSubmit handler through the props to the components which accepts two params(Username and Password). When I am calling the onsubmit handler I need to call handler with password and username password as params. I have wrote the logic for the same but when I am rendering I am not getting the textbox to fill (userName and Password). Any one can help me to sort out this issue? Thanks in advance. I have wrote down the code below.
function FormDetails(props) {
return (
<div>
<form onSubmitHandler={props.onSubmitHandler}>
<input type="text" id="user-input" name="userName" />
<input type="password" id="password-input" name="password" />
<button type="submit">Submit</button>
</form>
</div>
);
}
function LoginForm() {
const [form, setForm] = useState({
userName: "",
password: "",
});
const onSubmitHandler = (e) => {
e.preventDefault();
console.log("form.userName", form.userName);
setForm({ [e.target.name]: e.target.value });
};
if (form.userName && form.password == null) {
return <FormDetails onSubmitHandler={onSubmitHandler} />;
}
return (
<div>
UserName:{form.userName}
Password:{form.password}
</div>
);
}
export default LoginForm;
update your if condtion and form element to this
<form onSubmit={props.onSubmitHandler}>
if (!form.userName && !form.password) {
// other code
}
the if condition
the if condition should be revesed because you want to show the form if the values are falsy("" empty string, undefined, null ...)
if (!form.userName || !form.password) {
// ^-- ^--
return <FormDetails onSubmitHandler={onSubmitHandler} />;
}
moving values from child to parent
use a ref for the username & password fields
const userName = useRef(null);
const password = useRef(null);
pass the values up with the handleSubmit callback
<div>
<form
onSubmit={(e) => {
e.preventDefault();
console.log({
userName: userName.current.value,
password: password.current.value
});
props.onSubmitHandler({
userName: userName.current.value,
password: password.current.value
});
}}
>
<input ref={userName} type="text" id="user-input" name="userName" />
<input
ref={password}
type="password"
id="password-input"
name="password"
/>
<button type="submit">Submit</button>
</form>
</div>
Final result
import { useState, useRef } from "react";
function FormDetails(props) {
const userName = useRef(null);
const password = useRef(null);
return (
<div>
<form
onSubmit={(e) => {
e.preventDefault();
console.log({
userName: userName.current.value,
password: password.current.value
});
props.onSubmitHandler({
userName: userName.current.value,
password: password.current.value
});
}}
>
<input ref={userName} type="text" id="user-input" name="userName" />
<input
ref={password}
type="password"
id="password-input"
name="password"
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
function LoginForm() {
const [form, setForm] = useState({
userName: "",
password: ""
});
const onSubmitHandler = (val) => {
setForm(val);
};
if (!form.userName || !form.password) {
return <FormDetails onSubmitHandler={onSubmitHandler} />;
}
return (
<div>
UserName:{form.userName}
Password:{form.password}
</div>
);
}
export default LoginForm;
How is it possible to send these data (Taste, Trust, content of question all questions (which are in Tabs.jsx), everything except the description see the picture please) to my backend by clicking on 'Save the page'.In first part before putting any url inside, I really want to see the data in the console log for now I have nothing printed out except this message :
SyntheticBaseEvent {_reactName: "onClick", _targetInst: null, type: "click", nativeEvent: PointerEvent, target: HTMLButtonElement…}
I have 2 versions but I don't know the difference, what is better to use, I just saw these 2 ways in this forum.
export default function MenuItemDisplay() {
const [data, setData] = useState({
taste: "",
trust: ""
});
function onSubmit(e) {
e.preventDefault();
axios
.post("", {
trust: data.trust,
taste: data.taste
})
.then((res) => {
console.log(res.data);
});
}
function handleSubmit(e) {
const newData = { ...data };
newData[e.target.id] = e.target.value;
setData(newData);
console.log(e);
}
const onError = () => {
console.log("Error");
};
function Checkbox({ value }) {
const [checked, setChecked] = useState(true);
return (
<label>
<input
type="checkbox"
defaultChecked={checked}
onChange={() => setChecked(!checked)}
/>
{value}
</label>
);
}
return (
<>
<h1> {item.name} </h1>
<div>
<div className="TextStyle">
{"Taste "}
<Dropdown ... /> .
</div>
...
<Checkbox value={!!item.trust} />
</div>
...
<button
type="submit"
onClick= {handleSubmit}
> Save the content
</button>
</>
);
}
OR the same code with fetch :
export default function MenuItemDisplay() {
const [trust, setTrust] = useState("item.trust");
const [taste, setTaste] = useState("item.taste");
const [message, setMessage] = useState("");
let handle = async (e) => {
e.preventDefault();
try {
let res = await fetch("", {
method: "POST",
body: JSON.stringify({
trust: trust,
taste: taste
})
});
let resJson = await res.json();
if (res.status === 200) {
setTaste("");
setTrust("");
message("Success");
} else {
setMessage("Some error occured");
}
} catch (err) {
console.log(err);
}
};
return (
<>
<form onSubmit={hadle}>
<h1> {item.name} </h1>
<div>
<div className="TextStyle">
{"Taste "}
<Dropdown
style={styles.select}
options={TASTE}
defaultValue={TASTE.find((t) => t.label === item.taste)}
styleSelect={colourStyles}
isMulti={true}
/>
</div>
<div className="TextStyle">
{"Trust "}
<Checkbox value={!!item.trust} />
</div>
<div className="TextStyle"> Description : {item.description} </div>
<Tabs data={item.questions} />
</div>
<button>Save</button>
</form>
</>
);
}
Plus I have another form inside my MenuItemDisplay component that is Tabs.jsx. I really don't don't how can I deal with these 2 forms and how to 'trace' these data...
Here is my code
You can go with the following approaches.
Using a form element
You need to send the form data to the backend using AJAX calls. The most conventional way to do so is to use a JSX form element.
export default function MenuItemDisplay() {
...
return (
<form onSubmit={handleSubmit}>
<h1> {item.name} </h1>
<div>
<div className="TextStyle">
{"Taste "}
<Dropdown
style={styles.select}
options={TASTE}
defaultValue={TASTE.find((t) => t.label === item.taste)}
styleSelect={colourStyles}
isMulti={true}
/>
</div>
<div className="TextStyle">
{"Trust "}
<Checkbox value={!!item.trust} />
</div>
<div className="TextStyle"> Description : {item.description} </div>
<Tabs data={item.questions} />
</div>
<button type="submit"> Save the page</button>
</form>
);
}
Using a handleSubmit callback to the save button
Another way to do it using the button to send the request to the backend when clicked.
export default function MenuItemDisplay() {
...
return (
<>
<h1> {item.name} </h1>
<div>
<div className="TextStyle">
{"Taste "}
<Dropdown
style={styles.select}
options={TASTE}
defaultValue={TASTE.find((t) => t.label === item.taste)}
styleSelect={colourStyles}
isMulti={true}
/>
</div>
<div className="TextStyle">
{"Trust "}
<Checkbox value={!!item.trust} />
</div>
<div className="TextStyle"> Description : {item.description} </div>
<Tabs data={item.questions} />
</div>
<button onClick={handleSubmit}> Save the page</button>
</>
);
}
All you need to do is define the handleSubmit callback. If you use the form element, you'd need to prevent the default form submission behavior.
async function handleSubmit(e) {
e.preventDefault(); // if you use the form tag
// validate the data here
// use fetch or axios or any other 3rd party library to send data to the back end
// receive response from backend
// do something with the response
}
I'm new to React and Typescript and what I'm trying to do is, after successfully logging in I want to redirect the user to the homepage, but navigate doesn't seem to work.
Here is my login component:
function Login() {
const auth = useRecoilValue(authAtom);
const { register, handleSubmit, formState } = useForm<IFormValues>();
const navigate = useNavigate();
const { isSubmitting } = formState;
console.log(isSubmitting);
function onSubmit(values: IFormValues ) {
const baseUrl = `${process.env.REACT_APP_API_URL}/users/authenticate`;
const creds = {
Username: values.username,
Password: values.password
};
return authenticateApi(baseUrl, creds)
.then(X => {
navigate('/');
});
}
useEffect(() => {
// redirect to home if already logged in
if (auth) navigate('/');
}, []);
return (
<div className="col-md-6 offset-md-3 mt-5">
<div className="card">
<h4 className="card-header">Login</h4>
<div className="card-body">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group">
<label>Username</label>
<input type="text" {...register("username")} className={`form-control`} />
<div className="invalid-feedback"></div>
</div>
<div className="form-group">
<label>Password</label>
<input type="password" {...register("password")} className={`form-control`} />
<div className="invalid-feedback"></div>
</div>
<button disabled={isSubmitting} className="btn btn-primary">
{isSubmitting && <span className="spinner-border spinner-border-sm mr-1"></span>}
Login
</button>
</form>
</div>
</div>
</div>
)
}
I have been stuck on this for a while, so any help would be greatly appreciated.
Do you ever actually import useNavigate()? It comes from the react-router-dom package, and can be used like so:
import { useNavigate } from 'react-router-dom';
function Login() {
// ...
const navigate = useNavigate();
// ...
}
Try this one it might work! or you can create a new function to redirect user to homepage after login
import { useNavigate } from "react-router-dom";
function Login() {
//This is for navifating user to home page
const navigate = useNavigate();
const auth = useRecoilValue(authAtom);
const { register, handleSubmit, formState } = useForm<IFormValues>();
const { isSubmitting } = formState;
console.log(isSubmitting);
const onSubmit = (values: IFormValues ) => {
const baseUrl = `${process.env.REACT_APP_API_URL}/users/authenticate`;
const creds = {
Username: values.username,
Password: values.password
};
return authenticateApi(baseUrl, creds)
.then(X => {
navigate('/');
});
}
const auth = () => {
// redirect to home if already logged in
navigate('/');
};
return (
<div className="col-md-6 offset-md-3 mt-5">
<div className="card">
<h4 className="card-header">Login</h4>
<div className="card-body">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group">
<label>Username</label>
<input type="text" {...register("username")} className={`form-control`} />
<div className="invalid-feedback"></div>
</div>
<div className="form-group">
<label>Password</label>
<input type="password" {...register("password")} className={`form-control`} />
<div className="invalid-feedback"></div>
</div>
<button disabled={isSubmitting} className="btn btn-primary">
{isSubmitting && <span className="spinner-border spinner-border-sm mr-1"></span>}
Login
</button>
</form>
</div>
</div>
</div>
)
}