How to use UseEffect cleaning on async function for Firebase Login? - reactjs

I have not been able to use the useEffect function to not get the following warning:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates >a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a >useEffect cleanup function.
here's my code
import React, { useEffect, useRef, useState } from 'react';
import { Card, Button, Form, Alert } from 'react-bootstrap';
import { useAuth } from '../context/AuthContext';
import { Link, useHistory } from "react-router-dom";
export default function Login() {
const emailRef = useRef();
const passwordRef = useRef();
const { login } = useAuth();
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const history = useHistory();
async function handleSubmit(e) {
e.preventDefault();
try {
setError('')
setLoading(true)
await login(emailRef.current.value, passwordRef.current.value)
history.push('/')
} catch {
setError('Failed to Log In')
}
setLoading(false)
}
return (
<>
<Card>
<Card.Body>
<h2 className='text-center mb-4'>
Log In
</h2>
{error && <Alert variant='danger'>{error}</Alert>}
<Form onSubmit={handleSubmit}>
<Form.Group id='email'>
<Form.Label>
Email
</Form.Label>
<Form.Control type='email' ref={emailRef} required />
</Form.Group>
<Form.Group id='password'>
<Form.Label>
Password
</Form.Label>
<Form.Control type='password' ref={passwordRef} required />
</Form.Group>
<Button disabled={loading} className='w-100' type='submit'>
Log In
</Button>
</Form>
<div className='w-100 text-center mt-3'>
<Link to='/forgot-password'>Forgot Password?</Link>
</div>
</Card.Body>
</Card>
<div className='w-100 text-center mt-2'>
Need an account? <Link to='/signup'>Sign Up</Link>
</div>
</>
);
}
I have tried several things including:
import React, { useEffect, useRef, useState } from 'react';
import { Card, Button, Form, Alert } from 'react-bootstrap';
import { useAuth } from '../context/AuthContext';
import { Link, useHistory } from "react-router-dom";
export default function Login() {
const emailRef = useRef();
const passwordRef = useRef();
const { login } = useAuth();
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const history = useHistory();
useEffect(() => {
async function handleSubmit(e) {
e.preventDefault();
try {
setError('')
setLoading(true)
await login(emailRef.current.value, passwordRef.current.value)
history.push('/')
} catch {
setError('Failed to Log In')
}
setLoading(false)
}
handleSubmit();
return handleSubmit;
}, [])
return (
<>
<Card>
<Card.Body>
<h2 className='text-center mb-4'>
Log In
</h2>
{error && <Alert variant='danger'>{error}</Alert>}
<Form onSubmit={handleSubmit}>
<Form.Group id='email'>
<Form.Label>
Email
</Form.Label>
<Form.Control type='email' ref={emailRef} required />
</Form.Group>
<Form.Group id='password'>
<Form.Label>
Password
</Form.Label>
<Form.Control type='password' ref={passwordRef} required />
</Form.Group>
<Button disabled={loading} className='w-100' type='submit'>
Log In
</Button>
</Form>
<div className='w-100 text-center mt-3'>
<Link to='/forgot-password'>Forgot Password?</Link>
</div>
</Card.Body>
</Card>
<div className='w-100 text-center mt-2'>
Need an account? <Link to='/signup'>Sign Up</Link>
</div>
</>
);
}
Chrome also shows me a warning a bout a password leak every time I Login or Log out I´m not sure if it is because of this problem.
Please help

Your useEffect ( create one more ) should look like this :
let mounted = true;
useEffect(()=>{
return function cleanup(){
mounted = false
}
})
Now check for the mounted value while updating a state, especially the loading one

Related

React-Bootsrap Multiple Modals

I have a navbar with the usual login and sign-up buttons. When I click the relevant link, a modal with the form should pop-up allowing the user to login or sign-up. At the moment, neither of the pop-ups seem to work. I have been following React-Bootstrap Multiple Modal and have been using the chosen answer to try and implement the modals.
The handle functions are in my app.jsx:
import React, {useEffect, useState} from 'react'
import {useRouter} from 'next/router'
import {SessionProvider, useSession} from 'next-auth/react'
import {SSRProvider} from 'react-bootstrap'
import 'application.css';
import Navigation from "../components/Navigation";
import Login from "../components/Login";
import SideNav from "../components/SideNav";
import SignUp from "../components/SignUp";
function MyApp({Component, pageProps}) {
let id = '';
const [show, setShow] = useState(null)
function handleClose() {
setShow({show: id});
}
function handleShow(id) {
setShow({show: id});
}
const [showSideBar, setShowSideBar] = useState(false)
const toggleSideMenu = () => setShowSideBar((prev) => !prev);
return (
<SessionProvider session={pageProps.session}>
<SSRProvider>
<SideNav showSideBar={showSideBar} />
<Navigation
handleShow={handleShow}
handleClose={handleClose}
toggleSideMenu={toggleSideMenu}
/>
<Login
show={show}
handleShow={handleShow}
handleClose={handleClose}
/>
<SignUp
show={show}
handleShow={handleShow}
handleClose={handleClose}
/>
{Component.auth
? <Auth><Component {...pageProps} /></Auth>
: <Component {...pageProps} />
}
</SSRProvider>
</SessionProvider>
)
}
function Auth({children}) {
// #ts-ignore
const [session, loading] = useSession()
const isUser = !!session?.user
const router = useRouter()
useEffect(() => {
if (loading) return // Do nothing while loading
if (!isUser) router.push('/login')
// If not authenticated, force log in
}, [isUser, loading])
if (isUser) {
return children
}
// Session is being fetched, or no user.
// If no user, useEffect() will redirect.
return <div>Loading...</div>
}
export default MyApp
I pass the show, handleshow, handleclose to each component. In the navigation the buttons look like this:
<Nav.Link href="#" onClick={() => handleShow('login')}><FontAwesomeIcon icon={solid('sign-in-alt')}/><span> Login</span></Nav.Link>
<Nav.Link href="#" onClick={() => handleShow('signup')}><FontAwesomeIcon icon={solid('user-plus')}/><span> Sign up</span></Nav.Link>
And then finally, my Login.jsx with a modal:
import {getCsrfToken, signIn, useSession} from "next-auth/react";
import Router from "next/router";
import { Modal, CloseButton, Form } from 'react-bootstrap';
import Link from "next/link";
import { useState } from "react";
function Login({csrfToken, show, handleClose, handleShow, props}) {
const [error, setError] = useState(false);
//setShow(prev => !prev);
const handleSubmit = async (e) => {
e.preventDefault();
const res = await signIn('credentials', {
redirect: false,
email: e.target.email1.value,
password: e.target.password1.value,
callbackUrl: `/dashboard`,
});
if (res?.error) {
setError(true);
} else {
Router.push('/dashboard');
}
}
return (
<Modal show={show === "login"} onHide={handleClose} fade={false}>
<Modal.Header className={"modal-dark bg-dark"}>
<Modal.Title><h1>Log In</h1></Modal.Title>
<CloseButton variant="white" onClick={handleClose}/>
</Modal.Header>
<Modal.Body className={"modal-dark bg-dark"}>
<form noValidate onSubmit={(e) => handleSubmit(e)}>
<input name="csrfToken" type="hidden" defaultValue={csrfToken}/>
<Form.Group className="mt-3">
<Form.Control
id="email-address1"
name="email1"
type="email"
autoComplete="email"
required
className=""
placeholder="Email address"
/>
</Form.Group>
<Form.Group className="mt-3">
<Form.Control
id="password1"
name="password1"
type="password"
autoComplete="current-password"
required
placeholder="Password"
/>
</Form.Group>
<button type={"submit"} className={"btn orange-button mt-3"}>Login</button>
<div>
or <Link href='/signup'>sign up</Link>
</div>
{error && <div className="bg-red-300 p-2 text-white rounded">Wrong email or password</div>}
</form>
</Modal.Body>
<Modal.Footer className={"modal-dark bg-dark"}>
<button onClick={handleClose}>
Close
</button>
</Modal.Footer>
</Modal>
);
}
export default Login;
In the developer tools looking at the React components tab, it is sending the correct information.
The props all seem to be there, the modal just doesn't seem to pop-up. Thanks

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body

Hello I'mm new to coding and I want to make my signin function using redux, but I have a problem with my code. I'm getting the error:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
This is my code
import React, { useEffect, useState } from 'react'
import { Button, Container, Form } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
import ErrorMessageBox from '../components/ErrorMessagebox'
import Loading from '../components/Loading'
export const SignInScreen = () => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userLogin = useSelector((state) => state.userLogin)
const { userInfo, loading, error } = userLogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
return (
<Container className="small-container">
<h1 className="my-3">Sign In</h1>
{error && <ErrorMessageBox variant="danger">{error}</ErrorMessageBox>}
{loading && <Loading />}
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
required
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Group>
<div className="mb-3">
<Button type="submit">Sign In</Button>
</div>
<div className="mb-3">
New Customer?{' '}
<Link to={`/signup?redirect=${redirect}`}>Create new account</Link>
</div>
</Form>
</Container>
)
}
When I run npm ls react-dom, it shows
Plesae help, I don't know what this error means.
Sir. I think you made a typo and the error is due to that.
Change useNavigate to useNavigate()
import React, { useEffect, useState } from 'react'
import { Button, Container, Form } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
import ErrorMessageBox from '../components/ErrorMessagebox'
import Loading from '../components/Loading'
export const SignInScreen = () => {
const navigate = useNavigate()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userLogin = useSelector((state) => state.userLogin)
const { userInfo, loading, error } = userLogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
return (
<Container className="small-container">
<h1 className="my-3">Sign In</h1>
{error && <ErrorMessageBox variant="danger">{error}</ErrorMessageBox>}
{loading && <Loading />}
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
required
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Group>
<div className="mb-3">
<Button type="submit">Sign In</Button>
</div>
<div className="mb-3">
New Customer?{' '}
<Link to={`/signup?redirect=${redirect}`}>Create new account</Link>
</div>
</Form>
</Container>
)
}

Why might I be getting a: TypeError: updatePassword is not a function

I have a simple react app with an update profile page, using firebase for auth, and when I try to change the password, I'm getting this error:TypeError: updatePassword is not a function.
Here is my UpdateProfile.js
import React, { useRef, useState } from "react";
import { Form, Button, Card, Alert } from "react-bootstrap";
import { getAuth } from "firebase/auth";
import { Link, useNavigate } from "react-router-dom";
export default function UpdateProfile() {
const emailRef = useRef();
const passwordRef = useRef();
const passwordConfirmRef = useRef();
const { currentUser, updateEmail, updatePassword } = getAuth();
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
function handleSubmit(e) {
e.preventDefault();
if (passwordRef.current.value !== passwordConfirmRef.current.value) {
return setError("Passwords do not match");
}
const promises = [];
setLoading(true);
setError("");
if (emailRef.current.value !== currentUser.email) {
promises.push(updateEmail(emailRef.current.value));
}
if (passwordConfirmRef.current.value) {
promises.push(updatePassword(passwordRef.current.value));
}
Promise.all(promises)
.then(() => {
navigate("/");
})
.catch(() => {
setError("Failed to update account");
})
.finally(() => {
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="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
ref={emailRef}
required
defaultValue={currentUser.email}
/>
</Form.Group>
<Form.Group id="password" data-testid="pword">
<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>
<div className="w-100 mt-2">
<Button disabled={loading} className="w-100" type="submit">
Update
</Button>
</div>
</Form>
</Card.Body>
</Card>
<div className="w-100 text-center mt-2">
<Link to="/">Cancel</Link>
</div>
</>
);
}
the Error points to line 30, which is here in stars:
if (emailRef.current.value !== currentUser.email) {
promises.push(updateEmail(emailRef.current.value));
}
if (passwordConfirmRef.current.value) {
**promises.push(updatePassword(passwordRef.current.value));**
}
Can anyone see what I have wrong? Thanks!
The updateEmail and updatePassword functions can be imported from Firebase Auth SDK and not the Auth instance. Try updating your import statements to:
import { updateEmail, updatePassword } from "firebase/auth"

Why might my "setError and setLoading" be giving an undefined? (React project)

I'm adding authentication to a react app with a basic signup page, I have an async/await function to handle the submit and have the setError and setLoading declared with the useState hook, however I get an error saying:
Failed to compile
src/pages/Signup.js
Line 17:14: 'setError' is not defined no-undef
Line 20:7: 'setError' is not defined no-undef
Line 21:7: 'setLoading' is not defined no-undef
Line 24:7: 'setError' is not defined no-undef
Line 27:5: 'setLoading' is not defined no-undef
here is my code:
import React, { useRef, useState } from "react";
import { Form, Button, Card, Alert } from "react-bootstrap";
import { useAuth } from "../context/AuthContext";
export default function Signup() {
const emailRef = useRef();
const passwordRef = useRef();
const passwordConfirmRef = useRef();
const { signup } = useAuth();
const [error, serError] = useState("");
const [loading, serLoading] = useState(false);
async function handleSubmit(e) {
e.preventDefault();
if (passwordRef.current.value !== passwordConfirmRef.current.value) {
return setError("Passwords do not match");
}
try {
setError("");
setLoading(true);
await signup(emailRef.current.value, passwordRef.current.value);
} catch {
setError("Failed to create account");
}
setLoading(false);
}
return (
<>
<Card>
<Card.Body>
<h2 className="text-center mb-4">Sign Up</h2>
{error && <Alert variant="danger">{error}</Alert>}
<Form onSubmit={handleSubmit}>
<Form.Group id="email">
<Form.Label>Email</Form.Label>
<Form.Control type="email" ref={emailRef} required />
</Form.Group>
<Form.Group id="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password" ref={passwordRef} required />
</Form.Group>
<Form.Group id="password-confirm">
<Form.Label>Password Confirmation</Form.Label>
<Form.Control type="password" ref={passwordConfirmRef} required />
</Form.Group>
<Button disabled={loading} className="w-100" type="submit">
Sign Up
</Button>
</Form>
</Card.Body>
</Card>
<div className="w-100 text-center mt-2">
Already have an account? Log in
</div>
</>
);
}
I know it's easy to overlook something simple, can anyone figure out what I'm doing wrong? Thanks!
Your state is misspelled. const [error, serError] = useState(""); should be const [error, setError] = useState(""); and the same for setLoading, you have serLoading.

page redirect problem in react, react-router-dom

import React, { useState } from "react";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import Message from "../../components/Message/Message";
import Loader from "../../components/Loader/Loader";
import { login } from "../../actions/userActions";
export default function Login({ history }) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const dispatch = useDispatch();
const userLogin = useSelector((state) => state.userLogin);
const { loading, error, userInfo } = userLogin;
const submitHandler = (e) => {
e.preventDefault();
dispatch(login(email, password));
if (userInfo) {
if (userInfo.isAdmin) {
history.push("/admin/dashboard");
} else {
history.push("/");
}
}
};
return (
<div className="login-wrapper">
<div className="container py-5">
<div className="login-form p-5 rounded-2">
<h2 className="pb-3 text-center">Sign In</h2>
{error && <Message className="alert alert-danger">{error}</Message>}
{loading && <Loader />}
<form onSubmit={submitHandler}>
<div className="mb-3">
<label for="email" className="form-label">
Email address
</label>
<input
type="email"
value={email}
className="form-control"
id="email"
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div class="mb-3">
<label for="password" className="form-label">
Password
</label>
<input
type="password"
className="form-control"
value={password}
id="password"
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div className="d-grid ">
<button className="btn btn-submit p-2" type="submit">
CONTINUE
</button>
</div>
<div className="d-flex justify-content-between py-3">
<p className="">
New User?
<Link to="/register" className="ms-1">
Signup
</Link>
</p>
Forgot your password
</div>
</form>
</div>
</div>
</div>
);
}
After complete successful login and if a user is an admin I want to show /admin/dashboard in url. But it still showing the /login URL. Or user is not an admin I want to show the home page. it's working fine for a normal user. But for the admin URL is not update.
Note: After submitting the login form I store userInfo in localstorage. Then I get localstorage data for checking if user admin or not.
How can I solve this problem?
You need to redirect on your call back action in Login.
Example :
if (userInfo) {
if (userInfo.isAdmin) {
history.push("/admin/dashboard");
} else {
history.push("/");
}
}
In submitHandler After dispatch(login(email, password)); they not wait for the action complate and userInfo. isAdmin is still not available so It's not redirect to the /admin/dashboard page.
You can use the history object in your action and do that.
import React from 'react';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
export const App: React.FC = () => {
return (
<Router history={history}>
<Routes /> // Your all routes
</Router>
);
};
Now you can use history on your action.
import { history } from './app';
const login = () => {
// do API stuff
history.push('/somewhere');
history.push({
pathname: '/somewhere',
search: '?some=search-string',
hash: '#howdy',
state: {
[userDefined]: true
}
});
};
Or you can listen to the userInfo in useEffect hooks and do that.
useEffect(() => {
if (userInfo.isAdmin) {
history.push("/admin/dashboard");
} else {
history.push("/");
}
}, [userInfo?.isAdmin]);

Resources