Okay I'm trying to use global states to store if an user is logged in or not. To do so, I've created a Context file as follows:
import { createContext } from "react";
export const LoginContext= createContext({})
I also have my App.jsx:
import React, { useState } from 'react';
import Component1 from './Component1.jsx';
import Component2 from './Component2.jsx';
import Component3 from './Component3.jsx';
import { LoginContext } from '../Helper/Context.js';
function App(){
const [loggedIn, setLoggedIn] = useState(false);
return (
<LoginContext.Provider value={{loggedIn, setLoggedIn}}>
<Component1 />
<Component2 />
<Component3 />
</LoginContext.Provider>
)
}
export default App;
And then I have my Login component:
import React, {useState, useContext} from "react";
import Axios from 'axios';
import { Link, useHistory } from 'react-router-dom';
import { LoginContext } from "../Helper/Context";
import NavbarHome from "./NavbarHome";
function Login()
{
Axios.defaults.withCredentials = true;
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [loginStatus, setLoginStatus] = useState(false);
const {loggedIn, setLoggedIn} = useContext(LoginContext);
const [error, setErrorStatus] = useState("");
let history = useHistory();
let user = {
id: null,
nombre: null,
email: null
};
const login = () => {
Axios.post('http://localhost:3001/login', {
email: email,
password: password,
}).then((response)=>{
if(!response.data.auth) {
setLoginStatus(false);
setErrorStatus(response.data.message)
setLoggedIn(false);
}
else{
localStorage.setItem("token", response.data.token)
user = {
id: response.data.result[0].id,
name: response.data.result[0].name,
email: response.data.result[0].email
}
setLoginStatus(true);
setLoggedIn(true);
history.push('/perfil');
}
});
};
const userAuthenticated = () => {
Axios.get("http://localhost:3001/isUserAuth", {
headers: {
"x-access-token": localStorage.getItem("token"),
},
}).then((response) => {
console.log(response);
});
}
return(
<div>
<div>
<NavbarHome />
<div>
<div>
<h1>Login</h1>
<p className="label-login">Email:</p>
<input type="text" placeholder="Email..." onChange={(event) => {setEmail(event.target.value);}}/>
<p className="label-login">Contraseña:</p>
<input type="password" placeholder="Password..." onChange={(event) => {setPassword(event.target.value);}}/> <br />
<button onClick={login}>Login</button>
<p style={{marginTop: '1.3rem', color: 'red'}}>{error}</p>
<p><Link to='/registro'>Register here!</Link></p>
</div>
</div>
</div>
</div>
);
}
export default Login;
The output that I receive is this:
It complains about the line where I do this: setLoggedIn(false); or setLoggedIn(true); I'd like to use that state instead of setLoginStatus (which is the one I'm currently using)
Any ideas on how to fix it?
Related
It is my Auth Login form. I have AuthContext. Let's try log in.
I've clicked "log in" button. And we see AuthContext "user" : underfind
Then click reload page, and here we go, we have logged in
Why does it work like that?
Login.js
import React, {useState} from 'react'
import {useLogin} from '../hooks/useLogin'
import { useNavigate } from 'react-router-dom'
const Login = () => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const {login, error, isLoading} = useLogin()
let navigate = useNavigate()
const handleSubmit = async (e) => {
e.preventDefault()
await login(email, password)
}
return(
<div className='container'>
<h1>
login page
</h1>
<form className={'d-flex flex-column'} onSubmit={handleSubmit}>
<h3>Log in</h3>
<label>Email:</label>
<input
type={'email'}
onChange={(e) => setEmail(e.target.value)}
/>
<label>Password</label>
<input
type={'password'}
onChange={(e) => setPassword(e.target.value)}
/>
<button disabled={isLoading} type={'submit'}>
Log in
</button>
{error && <div className={'error'}>
{error}
</div>}
</form>
</div>
)
}
export default Login;
useLogin.js
import { useState } from "react";
import { useAuthContext } from "./useAuthContext";
export const useLogin = () => {
const [error, setError] = useState(null)
const [isLoading, setIsLoading] = useState(null)
const {dispatch} = useAuthContext()
const login = async (email, password) => {
setIsLoading(true)
setError(null)
const response = await fetch('/api/user/login', {
method:'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email, password})
})
const json = await response.json()
if(!response.ok){
setIsLoading(false)
setError(json.error)
}
if(response.ok){
localStorage.setItem('user', JSON.stringify(json))
dispatch({type:'LOGIN', paylaod:json})
setIsLoading(false)
}
}
return {login, isLoading, error}
}
AuthContext.js
import { createContext, useReducer, useEffect } from "react";
export const AuthContext = createContext()
export const authReducer = (state, action) => {
switch(action.type){
case 'LOGIN':
return {user: action.payload}
case 'LOGOUT':
return {user:null}
default:
return state
}
}
export const AuthContextProvider = ({children}) => {
const [state, dispatch] = useReducer(authReducer, {
user:null
})
useEffect(() => {
const user = JSON.parse(localStorage.getItem('user'))
if(user){
dispatch({type:'LOGIN', payload: user})
}
}, [])
console.log('AuthContext state', state)
return(
<AuthContext.Provider value={{...state, dispatch}}>
{children}
</AuthContext.Provider>
)
}
useAuthContext.js
import { AuthContext } from "../context/AuthContext";
import { useContext } from "react";
export const useAuthContext = () => {
const context = useContext(AuthContext)
if(!context){
throw Error('useAuthContext must be used inside an AuthContextProvider')
}
return context
}
here is Navbar.js
import React from "react";
import { useAuthContext } from "../hooks/useAuthContext";
import { useLogout } from "../hooks/useLogout";
import { Link } from "react-router-dom";
const Nav = () => {
const {logout} = useLogout()
const {user} = useAuthContext()
const handleClick= () => {
logout()
}
return(
<div>
{user && (
<div className="bg-dark text-light align-item-center d-flex justify-content-between m-auto container-fluid p-2">
<span className="m-0 p-0 d-flex align-item-center">{user.email}</span>
<button className="btn btn-outline-danger" onClick={handleClick}>logout</button>
</div>
)}
{!user && (
<div className="d-flex justify-content-between">
<Link to='/'>
<button>HOME</button>
</Link>
<Link to='/login'>
<button>LOGIN</button>
</Link>
<Link to='/signup'>
<button>SIGNUP</button>
</Link>
</div>
)}
</div>
)
}
export default Nav
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { AuthContextProvider } from './context/AuthContext';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<AuthContextProvider>
<App />
</AuthContextProvider>
</React.StrictMode>
);
app.js
import {BrowserRouter, Routes, Route} from 'react-router-dom'
import Nav from './components/Nav';
import Home from './pages/home';
import Login from './pages/login';
import Signup from './pages/signup';
function App() {
return (
<div>
<BrowserRouter>
<Nav/>
<Routes>
<Route path={'/'} element={<Home/>}/>
<Route path={'/login'} element={<Login/>}/>
<Route path={'/signup'} element={<Signup/>}/>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
I don't think it is server error.
There's a spelling mistake in useLogin payload is written wrong
dispatch({type:'LOGIN', paylaod:json});
// Should be: Payload
dispatch({type:'LOGIN', payload:json});
The root cause seems to be a typo:
dispatch({type:'LOGIN', paylaod:json})
has a typo in paylaod, so the reducer's
case 'LOGIN':
return {user: action.payload}
basically just sets {user: undefined}.
You should add error checking to your reducer, or better yet switch to a typed language like TypeScript so typos like these are caught at type-check time.
Since the user is not in the component state, it will not re-render after it is set (which seems to happen after first rendering). Add a useEffect with dependency on the value that updates the state and it should be good. F.ex:
const Nav = () => {
const {contextUser} = useAuthContext()
const [user, setUser] = useState()
...
useEffect(() => {
if (contextUser) {
setUser(contextUser)
}
}, [contextUser])
...
}
EDIT: Just saw that the post was very long so sorry and thanks a lot to whom can help me.
I'm using react as front in a project and I have a problem that I couldn't solve and didn't find anything on the internet.
So the problem is that I'm using jwt for login and when I log in on my page it's like it removes the state by himself, I'll show you:
(by the way I just started so there is no css)
login page:
logged in:
Then if I refresh the page or do anything like click on a button it will come back to the initial state like I never logged in and throw me to the login page because it doesn't see me logged in.
Here is the code:
authProvider.js:
import { createContext, useState } from "react";
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState(null);
console.log(auth);
return (
<AuthContext.Provider value={{ auth, setAuth }}>
{children}
</AuthContext.Provider>
)
}
export default AuthContext;
useAuth.js:
import { useContext } from "react";
import AuthContext from "../context/authProvider";
const useAuth = () => useContext(AuthContext);
export default useAuth;
login.js:
import React, { useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import useAuth from '../../hooks/useAuth';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { auth, setAuth } = useAuth();
const navigate = useNavigate();
const location = useLocation();
const from = location.state?.from?.pathname || '/';
const handleSubmit = (e) => {
e.preventDefault();
fetch('http://localhost:4000/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: email,
password: password
})
}).then(res => res.json())
.then(data => {
if (data.message) {
document.querySelector('#error').innerHTML = data.message;
}
setAuth({ email, password, token: data.token, refreshToken: data.refreshToken, role: data.role });
console.log(auth);
navigate(from, { replace: true });
});
}
return (
<>
<h1>Login</h1>
<span id='error'></span>
<form onSubmit={handleSubmit}>
<label>Email</label>
<input id="email" type="email" required onChange={(e) => { setEmail(e.target.value) }} />
<label>Password</label>
<input id="password" type="password" required onChange={(e) => { setPassword(e.target.value) }} />
<button type='submit'>se connecter</button>
</form>
</>
);
};
export default Login;
the image for the logs:
and nav.js just so you know how I know it works but state doesn't stay:
import { Link } from 'react-router-dom';
import useAuth from '../hooks/useAuth';
const Nav = () => {
const { auth } = useAuth();
return (
<>
<nav>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
{
!auth ?
<>
<li>
<Link to="/login">Login</Link>
</li>
<li>
<Link to="/signup">Signup</Link>
</li>
</>
: null
}
{
auth?.role === 'user' ?
<li>
<Link to="/profile">Profile</Link>
</li>
: null
}
{
auth?.role === 'admin' ?
<li>
<Link to="/admin">Admin</Link>
</li>
: null
}
</ul>
</div>
</nav>
</>
)
}
export default Nav;
I don't know if you need more but just say me if needed.
If I am using const currentUserId = props.id then the error will be props.match is undefined, and the variable "userId" even is undefined when i am trying to console log.
Then, I using props.match.params.id the code can't even being read, it's only blank
Im also trying to modify the code become const currentUserId = props.computedMatch.params.id; but nothing works
My code:
import React, {useContext, useState, useEffect} from 'react';
import { GlobalContext } from '../context/GlobalState';
import { Link, useNavigate } from 'react-router-dom';
import { v4 as uuid } from 'uuid';
import {
Form,
FormGroup,
Label,
Input,
Button
} from 'reactstrap';
export const EditUser = (props) => {
const [selectedUser, setSelectedUser] = useState({
id: '',
name: ''
});
const { users, editUser } = useContext(GlobalContext);
const history = useNavigate();
const currentUserId = props.match.params.id;
useEffect(() => {
const userId = currentUserId;
console.log(typeof userId);
const selectedUser = users.find(user => user.id === Number(userId))
setSelectedUser(selectedUser)
console.log(selectedUser);
}, [currentUserId, users])
const onSubmit = () => {
history('/');
}
const onChange = (e) => {
}
return (
<Form onSubmit={onSubmit}>
<FormGroup>
<Label>Nama</Label>
<Input type='text' onChange={onChange} placeholder='Ganti Nama Disini!'></Input>
</FormGroup>
<Button type='submit'>Edit Data</Button>
<Link to="/" className="btn btn-danger ml-2" style={{ marginLeft: "10px"}}>Cancel</Link>
</Form>
)
}
i hope this will fix
import { useParams } from 'react-router-dom';
const { id: currentUserId } = useParams();
use withRouter
import React, {useContext, useState, useEffect} from 'react';
import { GlobalContext } from '../context/GlobalState';
import { Link, useNavigate } from 'react-router-dom';
import { withRouter } from 'react-router'; // here added
import { v4 as uuid } from 'uuid';
import {
Form,
FormGroup,
Label,
Input,
Button
} from 'reactstrap';
const EditUser = (props) => { // here changed
const [selectedUser, setSelectedUser] = useState({
id: '',
name: ''
});
const { users, editUser } = useContext(GlobalContext);
const history = useNavigate();
const currentUserId = this.props.match.params.id;
useEffect(() => {
const userId = currentUserId;
console.log(typeof userId);
const selectedUser = users.find(user => user.id === Number(userId))
setSelectedUser(selectedUser)
console.log(selectedUser);
}, [currentUserId, users])
const onSubmit = () => {
history('/');
}
const onChange = (e) => {
}
return (
<Form onSubmit={onSubmit}>
<FormGroup>
<Label>Nama</Label>
<Input type='text' onChange={onChange} placeholder='Ganti Nama Disini!'></Input>
</FormGroup>
<Button type='submit'>Edit Data</Button>
<Link to="/" className="btn btn-danger ml-2" style={{ marginLeft: "10px"}}>Cancel</Link>
</Form>
)
}
export default withRouter(EditUser); // here added
So here I have Login functionality via local storage token. I am getting the token upon user being created in The dev tools/Application. It is redirecting me to the home as it should. The thing that is not working is this. When I try to press the logout I am getting ×TypeError: Cannot read property 'push' of undefined it is showing the error in the Logout Handler function
Context Api
import React from "react";
import { useHistory } from "react-router-dom"
const UserContext = React.createContext();
function getUserFromLocalStorage() {
return localStorage.getItem("authToken")
? JSON.parse(localStorage.getItem("authToken"))
: { username: null, token: null };
}
function UserProvider({ children }) {
const [user, setUser] = React.useState(getUserFromLocalStorage());
const history = useHistory()
const logoutHandler = () =>{
localStorage.removeItem("authToken");
setUser(user);
history.push("/")
}
return (
<UserContext.Provider
value={{ user, setUser, logoutHandler }}
>
{children}
</UserContext.Provider>
);
}
export { UserContext, UserProvider };
Login Link
import React from "react";
import { Link } from "react-router-dom";
import { UserContext } from "../../context/user";
import { useHistory } from "react-router-dom"
export default function LoginLink() {
const { user, logoutHandler } = React.useContext(UserContext);
if (user.authToken) {
return (
<button
onClick={() => {
logoutHandler();
}}
className="login-btn"
>
logout
</button>
);
}
return <Link to="/login">login</Link>;
Header
import React from 'react';
import { Link, useHistory } from "react-router-dom";
import Search from './Search';
import './Header.css'
import SearchBooks from './SearchBooks';
import LoginLink from '../Signin/LoginLink';
import CartLink from '../Cart/CartLink';
import { UserContext } from '../../context/user';
const Header = () => {
const { user } = React.useContext(UserContext);
return (
<div className='header__container'>
<Link to='/'>
<img src='/audi.png' />
</Link>
<li>
<LoginLink />
</li>
{user.authToken &&(
<CartLink />
)
}
</div>
)
}
export default Header
Login
import { useState, } from "react";
import { useHistory } from "react-router-dom"
import axios from "axios";
import { Link } from "react-router-dom";
import "./Signin.css";
const Login = () => {
const { user, setUser } = React.useContext(UserContext);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const history = useHistory()
const loginHandler = async (e) => {
e.preventDefault();
const config = {
header: {
"Content-Type": "application/json",
},
};
try {
const { data } = await axios.post(
"http://localhost:5000/api/auth/login",
{ email, password },
config
);
localStorage.setItem("authToken", data.token);
setUser(user)
history.push("/");
} catch (error) {
if (error.response) {
setError(error.response.data.error);
}
setTimeout(() => {
setError("");
}, 5000);
}
};
return (
<div className="login-screen">
<form onSubmit={loginHandler} className="login-screen__form">
<h3 className="login-screen__title">Login</h3>
{error && <span className="error-message">{error}</span>}
<div className="form-group">
<label htmlFor="email">Email:</label>
<input
type="email"
required
id="email"
placeholder="Email address"
onChange={(e) => setEmail(e.target.value)}
value={email}
tabIndex={1}
/>
</div>
<div className="form-group">
<label htmlFor="password">
Password:{" "}
<Link to="/forgotpassword" className="login-screen__forgotpassword">
Forgot Password?
</Link>
</label>
<input
type="password"
required
id="password"
autoComplete="true"
placeholder="Enter password"
onChange={(e) => setPassword(e.target.value)}
value={password}
tabIndex={2}
/>
</div>
<button type="submit" className="btn btn-primary">
Login
</button>
<span className="login-screen__subtext">
Don't have an account? <Link to="/register">Register</Link>
</span>
</form>
</div>
);
};
export default Login;
So here I have Login funcionality via local storage token. I am getting the token upon user being created in The dev tools/Application it is redirecting me to the home that is working. The thing that is not working is this. Creating an user it is working but when it loads it should show logout instead of login, and should hide the cart Component. Where am I making a mistake
Context Api
import React from "react";
const UserContext = React.createContext();
function getUserFromLocalStorage() {
return localStorage.getItem("authToken")
? JSON.parse(localStorage.getItem("authToken"))
: { username: null, token: null };
}
function UserProvider({ children }) {
const [user, setUser] = React.useState(getUserFromLocalStorage());
const history = useHistory();
const logoutHandler = () =>{
localStorage.removeItem("user");
history.push("/")
}
return (
<UserContext.Provider
value={{ user, logoutHandler }}
>
{children}
</UserContext.Provider>
);
}
export { UserContext, UserProvider };
Login Link
import React from "react";
import { Link } from "react-router-dom";
import { UserContext } from "../../context/user";
import { useHistory } from "react-router-dom"
export default function LoginLink() {
const { user, logoutHandler } = React.useContext(UserContext);
if (user.authToken) {
return (
<button
onClick={() => {
logoutHandler();
}}
className="login-btn"
>
logout
</button>
);
}
return <Link to="/login">login</Link>;
}
Header
import React from 'react';
import { Link, useHistory } from "react-router-dom";
import Search from './Search';
import './Header.css'
import SearchBooks from './SearchBooks';
import LoginLink from '../Signin/LoginLink';
import CartLink from '../Cart/CartLink';
import { UserContext } from '../../context/user';
const Header = () => {
const { user } = React.useContext(UserContext);
return (
<div className='header__container'>
<Link to='/'>
<img src='/audi.png' />
</Link>
<li>
<LoginLink />
</li>
{user.authToken &&(
<CartLink />
)
}
</div>
)
}
export default Header
Login Component
import { useState, } from "react";
import { useHistory } from "react-router-dom"
import axios from "axios";
import { Link } from "react-router-dom";
import "./Signin.css";
const Login = () => {
const { user, setUser } = React.useContext(UserContext);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const history = useHistory()
const loginHandler = async (e) => {
e.preventDefault();
const config = {
header: {
"Content-Type": "application/json",
},
};
try {
const { data } = await axios.post(
"http://localhost:5000/api/auth/login",
{ email, password },
config
);
localStorage.setItem("authToken", data.token);
setUser(user)
history.push("/");
} catch (error) {
if (error.response) {
setError(error.response.data.error);
}
setTimeout(() => {
setError("");
}, 5000);
}
};
return (
<div className="login-screen">
<form onSubmit={loginHandler} className="login-screen__form">
<h3 className="login-screen__title">Login</h3>
{error && <span className="error-message">{error}</span>}
<div className="form-group">
<label htmlFor="email">Email:</label>
<input
type="email"
required
id="email"
placeholder="Email address"
onChange={(e) => setEmail(e.target.value)}
value={email}
tabIndex={1}
/>
</div>
<div className="form-group">
<label htmlFor="password">
Password:{" "}
<Link to="/forgotpassword" className="login-screen__forgotpassword">
Forgot Password?
</Link>
</label>
<input
type="password"
required
id="password"
autoComplete="true"
placeholder="Enter password"
onChange={(e) => setPassword(e.target.value)}
value={password}
tabIndex={2}
/>
</div>
<button type="submit" className="btn btn-primary">
Login
</button>
<span className="login-screen__subtext">
Don't have an account? <Link to="/register">Register</Link>
</span>
</form>
</div>
);
};
export default Login;
After you login, you have only set the authToken into your localStorage and not in your Provider. You'll need to call setUser after you login in order for Header component to know user is logged in.