I'm learning reactjs and I'm trying to simulate an "Authentication" method on the front-end with reactjs and json-server and I'm facing a problem.
I have theses components:
Apps.js (with all the Routes)
Login.jsx ( with a form and all the logic )
ProtectedRoutes.jsx (as a function component to do a simple verification if the user is logged or no, and protected routes.
Clients.jsx (with all the lists fetched from the json-server, working properly, not important here)
I would like to create one state (isLogged / setIsLogged) to be trigged as "true" when the user hit the submit on my "Login.jsx", reusing this component, since the current state is "false". But I'm not figuring out how to do it. I'm not understanding how I can access the functions / state to do this.
App.js
import "./App.css";
import Login from "./pages/Login";
import Register from "./pages/Register";
import Clients from "./pages/Clients";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import ProtectedRoutes from "./components/ProtectedRoutes";
function App() {
return (
<>
<Router>
<Routes>
<Route path="/" element={<Login />} />
<Route element={<ProtectedRoutes isLogged={false} />}>
<Route path="/register" element={<Register />} />
<Route path="/clients" element={<Clients />} />
</Route>
</Routes>
</Router>
</>
);
}
export default App;
ProtectedRoutes.jsx
import { Navigate, Outlet } from "react-router-dom";
const ProtectedRoutes = ({ isLogged }) => {
return isLogged ? <Outlet /> : <Navigate to="/" />;
};
export default ProtectedRoutes;
Login.jsx
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { validateEmail, validatePassword } from "../utils/regex";
import logoImg from "../assets/logo-troupe.png";
import Navbar from "../components/Navbar";
const Login = () => {
const navigateTo = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [emailErr, setEmailErr] = useState(false);
const [passwordErr, setPasswordErr] = useState(false);
// input login validations
const validateEmailPassword = () => {
if (!validateEmail.test(email)) {
setEmailErr(true);
} else {
setEmailErr(false);
}
if (!validatePassword.test(password)) {
setPasswordErr(true);
} else {
setPasswordErr(false);
navigateTo("/clients");
}
};
//function to generate a random "token" simulating a login on backend
const handleStorageToken = () => {
const userToken = {
email,
password,
};
localStorage.setItem("Token", JSON.stringify(userToken));
};
const handleSubmit = (e) => {
e.preventDefault();
validateEmailPassword();
handleStorageToken();
};
return (
<div className="main-container">
<Navbar />
<div className="login-container">
<a href="#">
<img src={logoImg} alt="logo" tooltip="Troupe website" />
</a>
<h1>Login</h1>
<form>
<div className="form-group">
<label>E-mail</label>
<input
type="email"
placeholder="Enter your e-mail"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
{emailErr && (
<p className="validation-error">
Insira um e-mail válido!
</p>
)}
</div>
<div className="form-group">
<label>Password</label>
<input
type="password"
placeholder="Enter your password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{passwordErr && (
<p className="validation-error">
Senha inválida! Mínimo 4 caracteres, 1 letra and 1
número
</p>
)}
<button onClick={handleSubmit} className="btn-login" type="submit">
Login
</button>
</div>
</form>
</div>
</div>
);
};
export default Login;
Related
I am making a user context that has all of my user information that I need initially it is null and I update it in the login page but after I login and try to access the context from any other page it is still null
here is the code
CurrentUserContext.js
import { createContext } from "react";
export const CurrentUserContext = createContext(null);
UserContextLayout.jsx
import { Outlet } from "react-router-dom";
import { useState, useMemo, useCallback } from "react";
import { CurrentUserContext } from "../hooks/CurrentUserContext";
const UserContextLayout = () => {
const [currentUserContext, setCurrentUserContext] = useState(null);
return (
<CurrentUserContext.Provider
value={[currentUserContext, setCurrentUserContext]}
>
<Outlet />
</CurrentUserContext.Provider>
);
};
export default UserContextLayout;
MainRouter.jsx
import { Route, Routes } from "react-router-dom";
import Signin from "./pages/Signin";
import LandingPage from "./pages/LandingPage";
import { isAuthenticated } from "./apis/auth/auth-helper";
import UserContextLayout from "./utils/UserContextLayout";
const MainRouter = () => {
return (
<Routes>
<Route element={<UserContextLayout />}>
<Route path="/" element={<LandingPage />}></Route>
<Route
path="/signin"
element={
!isAuthenticated() ? <Signin /> : <Navigate to="/" replace={true} />
}
></Route>
</Route>
</Routes>
);
};
export default MainRouter;
Signin.jsx
import { useState, useContext } from "react";
import { GrTechnology } from "react-icons/gr";
import { CurrentUserContext } from "../hooks/CurrentUserContext";
import { signin } from "../apis/auth/auth-api";
import { authenticate } from "../apis/auth/auth-helper";
import { useNavigate } from "react-router-dom";
const Signin = () => {
const [currentUserContext, setCurrentUserContext] =
useContext(CurrentUserContext);
const navigate = useNavigate();
const [user1, setUser1] = useState({
email: "",
password: "",
});
const [rememberMe, setrememberMe] = useState(false);
const handleChange = (e) => {
setUser1((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
const handleRememberMeClick = () => {
setrememberMe((prev) => !prev);
};
const handleSubmit = async () => {
try {
const response = await signin(user1);
// if (res.status == 200) {
setCurrentUserContext(response.data.user);
authenticate(response.data.token, rememberMe);
navigate("/");
// } else {
// throw new Error("Authentication failed");
// }
} catch (error) {
console.log(error);
}
};
return (
<>
<div >
<div >
<h2 >
Sign in to your account
</h2>
<div >
<div>
<div>
<label htmlFor="email-address">
Email address
</label>
<input
id="email-address"
name="email"
type="email"
autoComplete="email"
required
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="password">
Password
</label>
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
onChange={handleChange}
/>
</div>
</div>
<div >
<div >
<input
id="remember-me"
name="remember-me"
type="checkbox"
onClick={handleRememberMeClick}
/>
<label
htmlFor="remember-me"
>
Remember me
</label>
</div>
</div>
<div>
<button
type="submit"
onClick={handleSubmit}
>
Sign in
</button>
</div>
</div>
</div>
</div>
</>
);
};
export default Signin;
response.data.user has the user data that I want to save in the context but the context is not updated when I call setCurrentUserContext
I am building an e-commerce react app and currently am stuck on redirecting to the home page once the user is logged in. Am using location to search the URL and split it and take the right part of it to go to the login page else if the user is logged in it should redirect to the home page.
But the problem is that even without a user logged in, the application just redirects straight to the home page. The login page is skipped and never displayed.
Below is my loginPage code.
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { Form, Button, Row, Col } from "react-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import Message from "../components/Message";
import Loader from "../components/Loader";
import FormContainer from "../components/FormContainer";
import { login } from "../actions/userActions";
const LoginScreen = ({ location, history }) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const dispatch = useDispatch();
const userLogin = useSelector((state) => state.userLogin);
const { loading, error, userInfo } = userLogin;
const redirect = location.search
? location.search.split("=")[1]
: "/";
useEffect(() => {
if (userInfo) {
history.push(redirect);
}
}, [history, userInfo, redirect]);
const submitHandler = (e) => {
e.preventDefault();
dispatch(login(email, password));
};
return (
<FormContainer>
<h1>Sign In</h1>
{error && <Message variant='danger'>{error}</Message>}
{loading && <Loader />}
<Form onSubmit={submitHandler}>
<Form.Group controlId='email'>
<Form.Label>Email Address</Form.Label>
<Form.Control
type='email'
placeholder='Enter email'
value={email}
onChange={(e) => setEmail(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId='password'>
<Form.Label>Password</Form.Label>
<Form.Control
type='password'
placeholder='Enter password'
value={password}
onChange={(e) => setPassword(e.target.value)}
></Form.Control>
</Form.Group>
<Button type='submit' variant='primary'>
Sign In
</Button>
</Form>
<Row className='py-3'>
<Col>
New Customer?{" "}
<Link to={redirect ? `/register?redirect=${redirect}` : "/register"}>
Register
</Link>
</Col>
</Row>
</FormContainer>
);
};
export default LoginScreen;
And Here is my App.js code.
import React from "react";
import { Container } from "react-bootstrap";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Header from "./components/Header";
import Footer from "./components/Footer";
import HomeScreen from "./screens/HomeScreen";
import ProductScreen from "./screens/ProductScreen";
import CartScreen from "./screens/CartScreen";
import LoginScreen from "./screens/LoginScreen";
const App = () => {
return (
<Router>
<Header />
<main>
<Container>
<Route path='/login' component={LoginScreen} />
<Route path='/product/:id' component={ProductScreen} />
<Route path='/cart/:id?' component={CartScreen} />
<Route path='/' component={HomeScreen} exact />
</Container>
</main>
<Footer />
</Router>
);
};
export default App;
The Login page is never displayed..
i finally got the problem. It was in the reducer store when i was setting the user to local storage. Instead of using null i was instead using [] and react was creating its own user.
const userInfoFromStorage = localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: [];
so i just changed it to null and everything works all fine.
const userInfoFromStorage = localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null;
login undefined
I am getting login as undefined, whenever I click the login button it was working fine before I created AppRoutes.js and moved some of the routes into that file.
TypeError: login is not a function
here is the code structure.
this is the main file where app is started and login route is put here inside the AppRoutes component.
app.js
import React, { Fragment, useEffect } from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import Navbar from './components/layout/Navbar'
import Landing from './components/layout/Landing'
import Alert from './components/layout/Alert'
import { loadUser } from './actions/auth'
import { Provider } from 'react-redux'
import setAuthToken from './utils/setAuthToken'
import store from './store'
import './App.css'
import AppRoutes from './components/routing/AppRoutes'
if (localStorage.token) {
setAuthToken(localStorage.token)
}
const App = () => {
useEffect(() => {
store.dispatch(loadUser())
}, [])
return (
<Provider store={store}>
<Router >
<Fragment>
<Navbar />
<Alert />
<Switch>
<Route exact path='/' component={Landing} />
<Route component={AppRoutes} />
</Switch>
</Fragment>
</Router>
</Provider>
)
}
export default App
2.AppRoutes.js
import React, { Fragment } from 'react'
import { Register } from '../auth/Register'
import { Login } from '../auth/Login'
import Dashboard from '../dashboard/Dashboard'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import PrivateRoute from './PrivateRoute'
import { NotFound } from '../layout/NotFound'
import store from '../../store'
import { Provider } from 'react-redux'
const AppRoutes = () => {
return (<Fragment>
<Switch>
<Route exact path='/register' component={Register} />
<Route exact path='/login' component={Login} />
<PrivateRoute path='/dashboard' component={Dashboard} >
</PrivateRoute>
<Route component={NotFound} />
</Switch>
</Fragment>
)
}
export default AppRoutes
auth.js
export const login = (email, password) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const body = JSON.stringify({ email, password })
try {
const res = await axios.post('/api/auth', body, config)
if (res?.data?.token) {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
})
dispatch(loadUser())
}
else {
dispatch(setAlert(res.data.msg, 'success'))
}
} catch (error) {
const errors = error.response.data.errors
if (errors) {
errors.forEach(error => dispatch(setAlert(error.msg, 'danger')))
}
dispatch({
type: LOGIN_FAIL
})
}
}
4.Login.js
import React, { Fragment, useState } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { login } from '../../actions/auth'
import { Link, Redirect } from 'react-router-dom'
import store from '../../store'
export const Login = ({ isAuthenticated, login }) => {
const [formData, setFormData] = useState({
email: '',
password: ''
})
const { email, password } = formData
const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value })
const onSubmit = e => {
e.preventDefault()
console.log(typeof login)
login(email, password)
}
//redirect if logged in
if (isAuthenticated) {
return <Redirect to='/dashboard' />
}
return (
<Fragment>
<div className="m-5">
<div className="row justify-content-center">
<div className="col-md-4">
<div className="card shadow-lg o-hidden border-0 my-5">
<div className="card-body p-0">
<div>
<div className="p-5">
<div className="text-center">
<h4 className="text-dark mb-4">Welcome Back!</h4>
</div>
<form className="user" onSubmit={e => onSubmit(e)}>
<div className="form-group">
<input className="form-control form-control-user" type="email" placeholder="Email Address" name="email" value={email} onChange={e => onChange(e)} required />
</div>
<div className="form-group">
<input className="form-control form-control-user" type="password"
placeholder="Password"
name="password"
minLength="6"
value={password} onChange={e => onChange(e)}
/>
</div>
<div className="form-group">
<div className="custom-control custom-checkbox small">
<div className="form-check">
<input className="form-check-input custom-control-input" type="checkbox" id="formCheck-1" />
<label className="form-check-label custom-control-label" htmlFor="formCheck-1">Remember Me</label>
</div>
</div>
</div><button className="btn btn-dark btn-block text-white btn-user" type="submit">Login</button>
<hr />
</form>
<div className="text-center"><Link to="/register" className="small" href="register.html">Create an Account!</Link></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</Fragment >
)
}
Login.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
}
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated
})
export default connect(mapStateToProps, { login })(Login)
I am not able to figure out whether it is related to the route structure that i changed or is there a bigger problem that I am missing out on.
You have imported Login a named import in AppRoutes, whereas the connected component with Login was a default export which is why you see the issue
Change
import { Login } from '../auth/Login'
to
import Login from '../auth/Login'
This is My App.js where all the Routes define Under Router. It's work fine when i jump from one Link to other in those component that are not using redux. but when i click on Redux connected component it's render component but then when i click on any other Link they just change Url Not view.
This is App js File:-
import React, { useEffect, Fragment } from "react";
import { Router, Route, Switch } from "react-router-dom";
import history from "./history";
import Navbar from "./components/layouts/Navbar";
import Landing from "./components/layouts/Landing";
import Profiles from "./components/profiles/Profiles";
import Login from "./components/auth/Login";
import Register from "./components/auth/Register";
import { loadUser } from "./actions/auth";
import { useDispatch } from "react-redux";
const App = () => {
const dispatch = useDispatch(() => loadUser());
useEffect(() => {
dispatch(loadUser());
}, [dispatch]);
return (
<Router history={history}>
<Navbar />
<Route exact path='/' component={Landing} />
<section className='container'>
<Alert />
<Switch>
<Route exact path='/register' component={Register} />
<Route exact path='/login' component={Login} />
<Route path='/profiles' component={Profiles} />
</Switch>
</section>
</Router>
);
};
export default App;
Both Register And LogIn Workimg well when navigating through each other but when I jump to component that using redux profiles, it loads and work but after that when i want to jump from profiles to Register login they just change url not view.
this is My profiles file that using redux and creating issue.
import React, { Fragment, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getProfiles } from "../../actions/profile";
import Spinner from "../layouts/Spinner";
import ProfileItems from "./ProfileItems";
import { withRouter } from "react-router-dom";
const Profiles = () => {
const profile = useSelector(state => state.profile);
const { profiles, loading } = profile;
const dispatch = useDispatch(() => getProfiles());
useEffect(() => dispatch(getProfiles()), [dispatch]);
return (
<Fragment>
{loading ? (
<Spinner />
) : (
<Fragment>
<h1 className='large text-primary'>Developers</h1>
<p className='lead'>
<i className='fab fa-connectdevelop'></i> Browse and Connect With
Developers...
</p>
<div className='profiles'>
{profiles.length > 0 ? (
profiles.map(profile => (
<ProfileItems key={profile._id} profile={profile} />
))
) : (
<h4>profile not Found !!!...</h4>
)}
</div>
</Fragment>
)}
</Fragment>
);
};
export default withRouter(Profiles);
And These are My Login And Register component that are working well when navigate to each other. when they go to profiles after that when i click on link of them they just change urls in address bar but not changing view. It's Login page Register is similar to this..
import React, { Fragment, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { login } from "../../actions/auth";
import { Link, Redirect } from "react-router-dom";
const Login = () => {
const dispatch = useDispatch(() => login());
const isAuthenticated = useSelector(state
=>state.auth.isAuthenticated);
const [formData, setFormData] = useState({
email: "",
password: ""
});
const { email, password } = formData;
const onChange = e => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
dispatch(login(email, password));
};
if (isAuthenticated) {
return <Redirect to='/dashboard' />;
}
return (
<Fragment>
<h1 className='large text-primary'>Sign In</h1>
<p className='lead'>
<i className='fas fa-user'>Sign In Your Account!!!</i>
</p>
<form onSubmit={e => onSubmit(e)} className='form'>
<div className='form-group'>
<input
type='email'
name='email'
placeholder='Enter Your Email'
value={email}
onChange={e => onChange(e)}
/>
</div>
<div className='form-group'>
<input
type='password'
name='password'
placeholder='Enter Your Password'
value={password}
onChange={e => onChange(e)}
/>
</div>
<div className='form-group'>
<input type='submit' value='LogIn' className='btn btn-primary' />
</div>
</form>
<p className='my-1'>
Don't have an Account <Link to='/register'>Sign Up</Link>
</p>
</Fragment>
);
};
export default Login;
I searched this alot and mostly got ans use withRouter I tried that one as u can see but still not working or maybe i am not using withRouter on correct component.
I'll do Provide any other information that you need to know about my code if you want and I am using react-redux hooks instead of using connect
I had faced the same problem in the past.
At that time I solve this issue with connected-react-router.
this is how to use the connected-react-router.
how to use connected-react-router
I am trying to create a shared global state for all components that an app needs, and instead of relying on props drilling or redux, I am trying to achieve that with the React Context.
Why does my user context not survive when I switch between routes? The application bellow illustrates the issue.
Do I need to use any other hook in conjunction with useContext?
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { AuthenticationProvider } from "./AuthenticationProvider";
const Index = () => {
return (
<AuthenticationProvider>
<App />
</AuthenticationProvider>
);
}
ReactDOM.render(<Index />, document.getElementById('root'));
//App.js
import React, { useState, useContext } from 'react';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import './App.css';
import { AuthenticationContext } from './AuthenticationProvider';
function AddUser() {
const [formUser, setFormUser] = useState("");
const [user, setUser] = useContext(AuthenticationContext);
const handleSubmit = async (event) => {
event.preventDefault();
setUser(formUser);
}
return (
<React.Fragment>
Form user: {formUser}.
<form id="form1" onSubmit={handleSubmit}>
<input type="text" id="user" onChange={event => setFormUser(event.target.value)} />
<input type="submit" value="Save" />
</form>
<br/>
Current user: {user}
<br/>
Back to home
</React.Fragment>
);
}
function Home() {
const [user, setUser] = useContext(AuthenticationContext);
return (
<React.Fragment>
<div className="App">
Hello {user}.
<br/>
Add user
</div>
</React.Fragment>
);
}
function App() {
return (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/add" component={AddUser} />
</Switch>
</Router>
);
}
export default App;
//AuthenticationProvider.js
import React, { useState, createContext } from "react";
const DEFAULT_STATE = "";
export const AuthenticationContext = createContext(DEFAULT_STATE);
export const AuthenticationProvider = ({ children }) => {
const [user, setUser] = useState(DEFAULT_STATE);
return (
<AuthenticationContext.Provider value={[user, setUser]} >
{children}
</AuthenticationContext.Provider>
);
}
The problem is that you used a regular <a> link to navigate through the app and every time you go from Home to addUser the app refreshes. To navigate through the app without refreshing the page use the Link component from react-router-dom
in Home and AddUser change the a links to the Link component
import { Link } from "react-router-dom";
function Home() {
const { user, setUser } = useContext(AuthenticationContext);
return (
<React.Fragment>
<div className="App">
Hello {user}.
<br />
<Link to="/add">Add user</Link> <-- Changed a to Link
</div>
</React.Fragment>
);
}
function AddUser() {
const [formUser, setFormUser] = useState("");
const [user, setUser] = useContext(AuthenticationContext);
const handleSubmit = async (event) => {
event.preventDefault();
setUser(formUser);
}
return (
<React.Fragment>
Form user: {formUser}.
<form id="form1" onSubmit={handleSubmit}>
<input type="text" id="user" onChange={event => setFormUser(event.target.value)} />
<input type="submit" value="Save" />
</form>
<br />
Current user: {user}
<br />
<Link to="/">Back to home</Link> <-- Changed a to Link
</React.Fragment>
);
}