react firebase auth login best practice - reactjs

I have a question regarding firebase auth best practices. Below is some simple code which handles user login. I want to know after the user logs in, what is the best / most efficient way of storing the user information in React. I have 3 options:
Storing in state the 'user' object which comes back in the .then
Storing in a context the 'user' object which comes back in the .then
Using the auth variable from firebase-config.js which comes with the currentUser object
Thanks
import React, { useState } from "react";
import { signInWithEmailAndPassword } from "firebase/auth";
import { auth } from "../firebase-config";
import { useNavigate } from "react-router-dom";
const Login = () => {
const [loginEmail, setLoginEmail] = useState("");
const [loginPassword, setLoginPassword] = useState("");
const [currentUser, setCurrentUser] = useState({});
const navigate = useNavigate();
const handleLogin = () => {
signInWithEmailAndPassword(auth, loginEmail, loginPassword)
.then((user) => {
setCurrentUser(user);
navigate("/");
})
.catch((err) => {
console.log(err.code);
err.code === "auth/wrong-password"
? alert("Incorrect password")
: alert(err.code);
});
};
return (
<>
<div className="form-container">
<p>
email
<input
type="email"
onChange={(e) => {
setLoginEmail(e.target.value);
}}
/>
</p>
<p>
password
<input
type="password"
onChange={(e) => {
setLoginPassword(e.target.value);
}}
/>
</p>
<button onClick={handleLogin}>Login</button>
</div>
</>
);
};
export default Login;

Related

Firebase email verification throws Invalid email link in reactjs

I'm trying to implement a firebase email verification process while signup using reactjs in my local. I have implemented the createUserWithEmailAndPassword default function along with the sendEmailVerification function. Once a new user is created, they are getting a confirmation email on their respective mail IDs but if the user tries to verify the email we are getting the below error as a response in signInWithEmailLink. We have tried with SSL in localhost 'https://localhost' but still the same issue.
Error
{code: 'auth/argument-error', message: 'Invalid email link!', a: null}
signin.js
import React, { useRef } from 'react'
import { auth } from '../firebase';
import './Signin.css'
const Signin = () => {
const emailRef = useRef(null);
const passwordRef = useRef(null);
const signUp = e => {
e.preventDefault();
auth.createUserWithEmailAndPassword(
emailRef.current.value,
passwordRef.current.value
).then(userCredential => {
userCredential.user.sendEmailVerification();
console.log(userCredential,'userCredential')
//auth.signOut();
return userCredential;
}).catch(err => {
console.log(err)
})
}
const signIn = e => {
e.preventDefault();
auth.signInWithEmailAndPassword(
emailRef.current.value,
passwordRef.current.value
).then(user => {
console.log(user)
}).catch(err => {
console.log(err)
})
}
const confirm = e =>{
const locationCode = window.location.href;
e.preventDefault();
auth.signInWithEmailLink(emailRef.current.value, locationCode)
.then((result) => {
console.log(result,'result')
})
.catch((err) => {
console.log(err,'err')
});
}
return (
<div className="signin">
<form action="">
<h1>Sign in</h1>
<input ref={emailRef} type="email" />
<input ref={passwordRef} type="password" />
<button onClick={signIn}>Sign in </button>
<button onClick={confirm}>confirm </button>
<h6>Not yet register? <span onClick={signUp} className="signin__link">Sign up</span></h6>
</form>
</div>
)
}
export default Signin

Error(Uncaught TypeError): Cannot read properties of undefined (reading 'params')

i am using react-router-dom v6 this code generating above error message ..please help me to solve the error ... backend is working fine ..i think this error is coming from fronted .... it works with postman ..
i am following a older tutorial ... now i installed new version of react-router-dom ... please help me out
this is ResetPassword.js file
import React, { Fragment, useState, useEffect } from "react";
import "./ResetPassword.css";
import Loader from "../layout/Loader/Loader";
import { useDispatch, useSelector } from "react-redux";
import { clearErrors, resetPassword } from "../../actions/userAction";
import { useAlert } from "react-alert";
import MetaData from "../layout/MetaData";
import LockOpenIcon from "#material-ui/icons/LockOpen";
import LockIcon from "#material-ui/icons/Lock";
const ResetPassword = ({ history, match }) => {
const dispatch = useDispatch();
const alert = useAlert();
const { error, success, loading } = useSelector(
(state) => state.forgotPassword
);
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const resetPasswordSubmit = (e) => {
e.preventDefault();
const myForm = new FormData();
myForm.set("password", password);
myForm.set("confirmPassword", confirmPassword);
dispatch(resetPassword(match.params.token, myForm));
};
useEffect(() => {
if (error) {
alert.error(error);
dispatch(clearErrors());
}
if (success) {
alert.success("Password Updated Successfully");
history.push("/login");
}
}, [dispatch, error, alert, history, success]);
return (
<Fragment>
{loading ? (
<Loader />
) : (
<Fragment>
<MetaData title="Change Password" />
<div className="resetPasswordContainer">
<div className="resetPasswordBox">
<h2 className="resetPasswordHeading">Update Profile</h2>
<form
className="resetPasswordForm"
onSubmit={resetPasswordSubmit}
>
<div>
<LockOpenIcon />
<input
type="password"
placeholder="New Password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div className="loginPassword">
<LockIcon />
<input
type="password"
placeholder="Confirm Password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
<input
type="submit"
value="Update"
className="resetPasswordBtn"
/>
</form>
</div>
</div>
</Fragment>
)}
</Fragment>
);
};
export default ResetPassword;
And The backend code is here
export const resetPassword = (token, passwords) => async (dispatch) => {
try {
dispatch({ type: RESET_PASSWORD_REQUEST });
const config = { headers: { "Content-Type": "application/json" } };
const { data } = await axios.put(
`/api/v1/password/reset/${token}`,
passwords,
config
);
dispatch({ type: RESET_PASSWORD_SUCCESS, payload: data.success });
} catch (error) {
dispatch({
type: RESET_PASSWORD_FAIL,
payload: error.response.data.message,
});
}
};
Thank you
In react-router-dom#6 the Route component API changed significantly. There are no longer any route props (i.e. no match or history props) all replaced by React hooks. The history object was replaced by a navigate function via the useNavigate hook, and route path params are accessible via the useParams hook.
Example:
import { useNavigate, useParams } from 'react-router-dom';
const ResetPassword = () => {
const navigate = useNavigate(); // <-- access navigate function
const { token } = useParams(); // <-- access token path parameter
...
const resetPasswordSubmit = (e) => {
...
dispatch(resetPassword(token, myForm)); // <-- use token param here
};
useEffect(() => {
...
if (success) {
alert.success("Password Updated Successfully");
navigate("/login"); // <-- call navigate here
}
}, [dispatch, error, alert, navigate, success]);

How to pass the API response data to another react page

Hi I have created a login page using React-Typescript. User login with username and password then click the submit button HttpPost Request call to my backend. If my response status succeeded. My response information back to return my React application. That response data i need store and pass the data to another page. That page call the get request. in that getrequest parameter i will send the response data.
For ex:
User successfully logged in. Backend send to the response(userId,userName). Then Redirect the Home page.
My home page have Httpget request(fetch("http:localhost:8080/api/getuser"). I need to pass the userId and userName in http request like(fetch("http:localhost:8080/api/getuser?userId="+userId"&userName="+userName)
Login tsx file:
import React from 'react';
import { Redirect } from 'react-router-dom';
import { SyntheticEvent, useState } from 'react';
import { Link } from 'react-router-dom';
const { REACT_APP_API_ENDPOINT } = process.env;
const Login = (props: { setName: (name: string) => void }) => {
const [userName, setuserName] = useState('');
const [userid, setuserId] = useState('');
const [password, setPassword] = useState('');
const [redirect, setRedirect] = useState(false);
const submit = async (e: SyntheticEvent) => {
e.preventDefault();
const response = await fetch(`${REACT_APP_API_ENDPOINT}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
userName,
password
})
});
const content = await response.json();
console.log(content);
setRedirect(true);
props.setName(content.userName);
setuserId(content.userId);
console.log(content.userId);
}
if (redirect) {
return <Redirect to="/" />
}
return (
<div>
<form onSubmit={submit} className="form-signin">
<h1 className="h3 mb-3 fw-normal">Please sign in</h1>
<div>
<input type="userName" className="form-control" placeholder="UserName" required
onChange={e => setuserName(e.target.value)} />
</div>
<div>
<input type="password" className="form-control" placeholder="Password" required
onChange={e => setPassword(e.target.value)} />
</div>
<button className="w-100 btn btn-lg btn-primary" type="submit">Log in</button>
</form>
</div>
);
};
export default Login;
App tsx file
import React, { Component } from 'react';
function Home() {
const { REACT_APP_API_ENDPOINT } = process.env;
const [name, setName] = useState('');
const [id, setId] = useState('');
useEffect(() => {
(
async () => {
const response = await fetch(`${REACT_APP_API_ENDPOINT}/getuser`, {
headers: { 'Content-Type': 'application/json' }
})
.then(function (response) {
console.log(response);
})
const content = await response;
//setName(content.name);
}
)();
});
return ();
export default Home;
There can be multiple ways of passing data from one component to another :
Using Props. You can use props to pass data from parent to child component. ...
|--- App.js
|---- ParentComponent
|----ChildComponent
Using React ContextAPI or State management library like Redux. ...
Using Props as Callback Function.
<Link
to={{
pathname: "/page",
data: data // your data array of objects
}}
>
//in /page
render() {
const { data } = this.props.location
return (
// render logic here
)
}
Okay. So, you want to send userId and userName from your Login component to Home component? You can send props from Login component via Redirect and access these props in Home component using useLocation hook.
//Login.jsx
<Redirect
to={{
pathname: "/",
state: { userId, userName }
}}
/>
//-------------------------------------
//App.jsx
import { useLocation } from 'react-router-dom';
const location = useLocation();
const { userId, userName } = location.state;

react login and sign up pages with auth using firebase

I am building Login and Signup pages using react and auth by using firebase.
The problem is that after successful login and signup, the application is NOT navigating the user to landing page.
I think there is something wrong with my handleLogin and handleSignup methods but, I'm to pinpoint it out.
To Login: use -> email: test#gmail.com and password: 123456
Also, there are no errors in the console and I get 200 response from firebase.
This is firebase.config.js
import firebase from "firebase";
var firebaseConfig = {
apiKey: "MY_KEY",
authDomain: "login-and-push-notification.firebaseapp.com",
projectId: "login-and-push-notification",
storageBucket: "login-and-push-notification.appspot.com",
messagingSenderId: "84083076850",
appId: "1:84083076850:web:3e9febc1341d8f7b036a6b",
};
// Initialize Firebase
const fb = firebase.initializeApp(firebaseConfig);
export default FB;
This is Login.js
const Login = (props) => {
const [user, setUser] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [emailError, setEmailError] = useState("");
const [passwordError, setPasswordError] = useState("");
const [hasAccount, setHasAccount] = useState(false);
const clearInputs = () => {
setEmail("");
setPassword("");
};
const clearErrors = () => {
setEmailError("");
setPasswordError("");
};
const handleLogin = () => {
clearErrors();
fb.auth()
.signInWithEmailAndPassword(email, password)
.then((user) => {
console.log("user", user);
props.navigation.push("/Landing");
})
.catch((error) => {
switch (error.code) {
case "auth/invalid-email":
case "auth/user-disabled":
case "auth/user-not-found":
setEmailError(error.message);
break;
case "auth/wrong-password":
setPasswordError(error.message);
break;
}
});
};
const handleSignup = () => {
clearErrors();
fb.auth()
.createUserWithEmailAndPassword(email, password)
.catch((error) => {
switch (error.code) {
case "auth/email-already-in-use":
case "auth/invalid-email":
setEmailError(error.message);
break;
case "auth/weak-password":
setPasswordError(error.message);
break;
}
});
};
const authListener = () => {
fb.auth().onAuthStateChanged((user) => {
if (user) {
clearInputs();
setUser(user);
} else {
setUser("");
}
});
};
useEffect(() => {
authListener();
}, []);
return (
<div className="login">
<div className="loginContainer">
<label>Username</label>
<input
type="text"
value={email}
autoFocus
required
onChange={(e) => setEmail(e.target.value)}
/>
<p className="errorMsg">{emailError}</p>
<label>Password</label>
<input
type="password"
value={password}
required
onChange={(e) => setPassword(e.target.value)}
/>
<p className="errorMsg">{passwordError}</p>
<div className="btnContainer">
{hasAccount ? (
<span>
<button onClick={handleLogin} type="submit">
Sign In
</button>
<p>
Don't have an account?
<span onClick={() => setHasAccount(!hasAccount)}>Sign up</span>
</p>
</span>
) : (
<>
<button onClick={handleSignup} type="submit">
Sign Up
</button>
<p>
Have an account?
<span onClick={() => setHasAccount(!hasAccount)}>Sign in</span>
</p>
</>
)}
</div>
</div>
</div>
);
};
And then I'm importing the Login component into the Home component and passing props.
import React from "react";
import Login from "../components/Login";
import Landing from "../Pages/Landing";
function Home(props) {
return (
<div>{props.user ? <Landing {...props} /> : <Login {...props} />}</div>
);
}
export default Home;
Your authListener() in Login.js is setting a state variable in the Login component, but it does not communicate the change of state back to the rest of the application.
One way to resolve this would be to move the const [user, setUser] = useState() from Login.js to Home.js, then pass setUser as a prop to <Login>. That way, when the onAuthStateChanged() gets the user object, you set it in state at the Home level.
You'd additionally change the condition statement in Home.js to be:
return (
<div>{user ? <Landing {...props} /> : <Login {...props} />}</div>
);
since user would now be a state variable in Home.js.
Once you have that working and are comfortable with it, you might consider moving all of your authentication handling into a pattern that leverages useContext(). You would create an AuthProvider component. There are several blogs on this topic, such as this one

Correct way handle client making POST api calls in NextJS application

I have a react application with redux. In the app there is a text field and a button. When the button is clicked an action creator is dispatched.
Here is an example of the component
import {useState} from "react";
import {useDispatch, useSelector} from "react-redux";
import updateEmail from "redux/actions/updateEmail";
const MyForm = () => {
const [email, setEmail] = useState("")
const dispatch = useDispatch()
const {saving, error} = useSelector(state => state.saveEmail)
if (saving) return <div>Please wait.....</div>
if (error) return <div>Something has gone wrong.....</div>
return (
<div className="container">
<p>Forgot Password</p>
<input
value={email}
onChange={e => setEmail(e.target.value)}
className="input is-large"
type="text"
placeholder="Email"
></input>
<a onClick={() => dispatch(updateEmail())} className="has-text-weight-bold">Login</a>
</div>
);
};
export default MyForm;
when the redux state saving, error changes, the UI re-renders. The state defines what the user is displayed.
In a NextJS application, how would I go about doing this without using redux.
What is the correct way without redux
for the client to make rest calls similar to above
the UI to re-render based on the state similar to above example
I don't really understand what the form is, is it for Login? I suppose it's for submitting Email for Password Reset. I commented you Answer too, so you can reconsider the Redux way for NextJS.
Anyway here is the proper way, which is the normal React way to do it in NextJS:
import { useState } from 'react';
import axios from 'axios';
// forget redux
// import {useDispatch, useSelector} from "react-redux";
// import updateEmail from "redux/actions/updateEmail";
const MyForm = () => {
const [email, setEmail] = useState('');
// 2 new states are created just for the form
// const { saving, error} = useSelector(state => state.saveEmail)
const [saving, setSaving] = useState(false);
const [error, setError] = useState(false);
// the dispatch (which has a function defined at another place)
// is replaced by a local function
// const dispatch = useDispatch()
const updateEmail = () => {
setSaving(true);
setError(false);
axios({
method: 'post',
url: `https://example.com/api/submit-email`,
headers: {
'Content-Type': 'application/json',
},
data: {
email,
},
})
.then(response => {
console.log('response login', response.data);
// NEXT STEP
})
.catch(err => {
console.log('login user error', err.response);
setSaving(false);
setError(true);
});
};
if (saving) return <div>Please wait.....</div>;
if (error) return <div>Something has gone wrong.....</div>;
return (
<div className="container">
<p>Forgot Password</p>
<input
value={email}
onChange={e => setEmail(e.target.value)}
className="input is-large"
type="text"
placeholder="Email"
/>
<a onClick={updateEmail} className="has-text-weight-bold">
Submit Email
</a>
</div>
);
};
export default MyForm;

Resources