I must add login to navbar in React - reactjs

I must add login system to navbar, but when I login in anywhere, navbar does not appear. How can I solve it?
Navbar shows normally with no login system.
Navbar is on App.js.
authLinks - Section just for authenticated users (Dashboard and Log out buttons)
guestLinks - Section just for not-authenticated users (Login form in here)
My navbar + login:
import React, { Fragment, useState } from "react";
import { Link, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { logout } from "../../actions/auth";
import { login } from "../../actions/auth";
const Navbar = ({ auth: { isAuthenticated, loading }, login, logout }) => {
const [formData, setFormData] = useState({
email: "",
password: ""
});
const { email, password } = formData;
const onChange = e =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = async e => {
e.preventDefault();
login(email, password);
};
if (isAuthenticated) {
return <Redirect to="/dashboard" />;
}
const authLinks = (
<ul>
<li>
<Link to="/dashboard">
<i className="fas fa-user" /> <span className="hide-sm">Dashboard</span>
</Link>
</li>
<li>
<a onClick={logout} href="#!">
<i className="fas fa-sign-out-alt" />{" "}
<span className="hide-sm">Log out</span>
</a>
</li>
</ul>
);
const guestLinks = (
<form
className="form-inline my-2 my-lg-0 navbar-login"
onSubmit={e => onSubmit(e)}
>
<div className="form-group">
<input
className="form-control mr-sm-2"
type="email"
placeholder="Email Address"
name="email"
value={email}
onChange={e => onChange(e)}
/>
</div>
<div className="form-group">
<input
className="form-control mr-sm-2 my-sm-0"
type="password"
placeholder="Password"
name="password"
value={password}
onChange={e => onChange(e)}
/>
</div>
<input type="submit" className="btn btn-primary" value="Login" />
</form>
);
return (
<nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<h1>
<Link className="navbar-brand" to="/">
<i className="fas fa-crown"></i>
<br />
nawdic
</Link>
</h1>
{!loading && (
<Fragment>{isAuthenticated ? authLinks : guestLinks}</Fragment>
)}
</nav>
);
};
Navbar.propTypes = {
logout: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
};
const mapStateToProps = state => ({
auth: state.auth,
isAuthenticated: state.auth.isAuthenticated
});
export default connect(mapStateToProps, { login, logout })(Navbar);

When user is logged in Navbar returns redirect component which is not the navbar itself
if (isAuthenticated) {
return <Redirect to="/dashboard" />;
}
Since isAuthenticated is passed to Navbar it shouldn't be responsible for redirecting in this case.
Try to move the Redirect thing to router as a best practice

Related

Why doesn't my React app redirect after login?

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>
)
}

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]);

How to manage the state using Redux

I'm working on a project, I'm using Django as backend, React as frontend and Redux as state manager. I want to pass a function from my login and register component to my Header component, whereas my header component is not a root component I can't pass params directly to other components. I know redux can solve this problem but I don't know how?
I have two separate links in my header component (Login, Register), I mean when I click on login it should call the openLoginModal() from my login component and the same process for register component
My header component
import React, { Component, Fragment } from "react";
import { Link } from "react-router-dom";
export class Header extends Component {
render() {
return (
<Fragment>
<nav className="navbar navbar-expand-sm ">
<div className="container">
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarTogglerDemo01"
aria-controls="navbarTogglerDemo01"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarTogglerDemo01">
<a className="navbar-brand" href="/">
Todos
</a>
<ul className="navbar-nav ml-auto mt-2 mt-lg-0">
<Link to="/register">
<button className="btn btn-outline-primary btn-sm mx-2">
Register
</button>
</Link>
<Link to="/login">
<button className="btn btn-outline-primary btn-sm">
Login
</button>
</Link>
</ul>
</div>
</div>
</nav>
</Fragment>
);
}
}
export default Header;
I also used react-awesome-modal for creating login and register modal, here is my login component
import React, { Component, Fragment } from "react";
export class Login extends Component {
constructor(props) {
super(props);
this.state = {
visible: false,
username: "",
password: "",
};
}
openLoginModal() {
this.setState({
visible: true,
});
}
closeLoginModal() {
this.setState({
visible: false,
});
}
render() {
const { username, password } = this.state;
return (
<Fragment>
<section>
<Modal
visible={this.state.visible}
width="400"
height="300"
effect="fadeInUp"
onClickAway={() => this.closeLoginModal()}
>
<div>
<form>
<div className="form-group">
<label>Username</label>
<input
type="text"
className="form-control"
name="username"
value={username}
/>
</div>
<div className="form-group">
<label>Password</label>
<input
type="password"
className="form-control"
name="password"
value={password}
/>
</div>
<div className="form-group">
<button className="btn btn-primary btn-sm my-2">Login</button>
</div>
</form>
</div>
</Modal>
</section>
</Fragment>
);
}
}
export default Login;
Just only store variable isOpenModal into the state of redux and add 2 action creator
openModal() {
return { type: 'OPEN_MODAL' }
}
closeModal {
return { type: 'CLOSE_MODAL'
}
change state through reducers (file reducers)
// another code
switch(action.type) {
case 'OPEN_MODAL':
state.isOpenModal = true;
break;
case 'CLOSE_MODAL':
state.isOpenModal = false;
break;
// another code
}
In file Header use mapDispatchToProps to call 2 action creator openModal and closeModal as created above.
In file Login use mapStateToProps to use state isOpenModal in state of redux.

how to redirect back to App with React.js from a component?

i'm new learner of React.js and i don't finding the right solution.
i have the CreateUser component and when a client success at creating one i want to Redirect the client to App...
i need it to happend in this function of CreateUser Component
private handleSubmit(e:any){
e.preventDefault();
this.setState({
username: this.state.username,
password: this.state.password,
confirmPassword: this.state.confirmPassword,
userEmail: this.state.userEmail
})
this.passConfrim();
if (this.isAlertVisible){
console.log(this.state)
this.myUserDateService.create(this.state);
----> ** Right Here i need to redirect! ** <----
}
}
in the end of the function at the if statement
App:
import './App.css';
import { LoginComponent } from './components/LoginComponent';
import CreateUser from './components/CreateUser';
import "bootstrap/dist/css/bootstrap.min.css";
import { Router, Switch, Route, Link, useHistory as history} from "react-router-dom";
function App() {
return (
<Router history={history()} >
<nav className="navbar navbar-expand navbar-dark bg-dark">
<a href="/Home" className="navbar-brand">
To Do List
</a>
<div className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/Login"} className="nav-link">
Login
</Link>
</li>
<li className="nav-item">
<Link to={"/CreateUser"} className="nav-link">
Create User
</Link>
</li>
</div>
</nav>
<div id="App-content">
<Switch >
<Route exact path={["/", "/Home"]} />
<Route path="/Login" exact component={LoginComponent} />
<Route path="/CreateUser" exact component={CreateUser} />
</Switch>
</div>
</Router>
);
}
export default App;
CreateUser Component:
import React, { Component } from 'react';
import { UserDataService } from '../services/UserData.service';
interface IState {
username:string;
userEmail:string;
password:string;
confirmPassword:string;
}
export class CreateUser extends Component <{}, IState> {
isAlertVisible: boolean = true;
myUserDateService = new UserDataService();
constructor(props: {}, myUserDateService:UserDataService){
super(props );
this.state = {
username:"",
password:"",
confirmPassword:"",
userEmail:"",
}
}
private handleSubmit(e:any){
e.preventDefault();
this.setState({
username: this.state.username,
password: this.state.password,
confirmPassword: this.state.confirmPassword,
userEmail: this.state.userEmail
})
this.passConfrim();
if (this.isAlertVisible){
console.log(this.state)
this.myUserDateService.create(this.state);
}
}
passConfrim(){
if(this.state.password !== this.state.confirmPassword){
this.isAlertVisible = false;
}else{
this.isAlertVisible = true;
}
}
render() {
return (
<div className="form-group">
<h1>Create User</h1>
<form onSubmit={e => this.handleSubmit(e)}>
<label >Username</label>
<input className="form-control" type="text" placeholder='Enter Username...' onChange={e => this.setState({username : e.target.value})} required/>
<br/>
<label >Email</label>
<input className="form-control" type="text" placeholder='Enter your email...' onChange={e => this.setState({userEmail : e.target.value})} required/>
<br/>
<label >Passowrd</label>
<input className="form-control" type="password" placeholder='Enter Password...' onChange={e => this.setState({password : e.target.value})} required/>
<br/>
<label >Confirm Passowrd</label>
<input className="form-control" type="password" placeholder='Confirm Password...' onChange={e => this.setState({confirmPassword : e.target.value })} required />
<div style={{color: "red", textAlign: "left"}} hidden={this.isAlertVisible}>**password not match</div>
<br/>
<button className="btn btn-primary" type="submit" >Create User</button>
</form >
</div>
)
}
}
export default CreateUser;
Basically you need not pass history with Router instead you can use withRouter high order component from react-router.
Import withRouter inside createUser component - https://reacttraining.com/react-router/core/api/withRouter
import { withRouter } from "react-router";
Then we just need to export CreateUser component like -
export default withRouter(CreateUser);
Now you have access to all props related to routing inside CreateUser component, now you can use -
this.props.history.push('/your-route');
To check what else properties you have with withRouter, you can just console.log this.props.history inside CreateUser component.
Tip - You cannot use hooks inside class components, so you cannot use useHistory inside CreateUser component instead use withRouter.
you can use history.push('/yourRoute') and that will take you to whatever route your heart so desires
Since you are extending the user component from react, it is a class component and you cannot use 'useHistory' hooks inside it.
Also you are passing history as a prop to router, can you try the below code to navigate and let me know.
this.props.history.push('/yourroute');

React: POST API call embedding wrong path

I created my project using CRA react CLI and I have stored data in a local JSON file in the public folder.
I have a Login Component. This is where I am making all the POST API calls using Axios and I've also have some other stuff. This component is later being imported inside Auth Layout.
The problem is: On submit, it is sending the POST request to the wrong path. It should send to /data/api/v1/login.json instead it is sending to http://localhost:3000/auth/data/api/v1/login.json.
I think this could be since the login component is loading from the auth layout. but, not sure how to resolve it.
Login component
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
//api calls
import axios from "axios";
import { setUserSession } from "../../Common/Utils/Common.js";
import { Button, Form, Input, InputGroup, InputGroupAddon } from "reactstrap";
const Login = (props) => {
const [loading, setLoading] = useState(false);
const username = useFormInput("");
const password = useFormInput("");
const [error, setError] = useState(null);
const handleLogin = () => {
setError(null);
setLoading(true);
axios
.post("data/api/v1/login.json", {
username: username.value,
password: password.value,
})
.then((response) => {
console.log("response", response);
setLoading(false);
setUserSession(response.data.token, response.data.user);
props.history.push("/admin/dashboard");
})
.catch((error) => {
setLoading(false);
if (error.response.status === 401)
setError(error.response.data.message);
else setError("Something went wrong. Please try again later.");
});
};
return (
<div className="container-fluid backgroundContainer">
<div className="Login">
<div className="login-form-container">
<div className="logo">
<img src={Logo} className="App-logo" alt="logo" />
</div>
<div className="content">
<Form className="login-form">
<h3 className="form-title">Welcome</h3>
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-user"></i>
</InputGroupAddon>
<Input
autoFocus
type="email"
aria-label="Username"
aria-describedby="username"
aria-invalid="false"
placeholder="Username or Email"
{...username}
/>
</InputGroup>
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-lock"></i>
</InputGroupAddon>
<Input
value={password}
placeholder="Password"
aria-label="password"
aria-describedby="password"
{...password}
// onChange={(e) => setPassword(e.target.value)}
type="password"
/>
</InputGroup>
<div className="form-actions">
{error && (
<>
<small style={{ color: "red" }}>{error}</small>
<br />
</>
)}
<br />
<button
className="pull-right"
block="true"
type="submit"
value={loading ? "Loading..." : "Login"}
onClick={handleLogin}
disabled={loading}
>
Login
</button>
<br />
</div>
<div className="forgotPassword">
<Link to="/auth/resetpassword">Forgot password?</Link>
</div>
</Form>
</div>
</div>
</div>
</div>
);
};
const useFormInput = (initialValue) => {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
return {
value,
onChange: handleChange,
};
};
export default Login;
Auth layout
import React from "react";
import Login from "../../components/pages/login/Login";
class Pages extends React.Component {
render() {
return (
<div className="wrapper wrapper-full-page" ref="fullPages">
<div className="full-page">
<Login {...this.props}></Login>
</div>
</div>
);
}
}
export default Pages;
Common.js this is a common service using in the login component.
// return the user data from the session storage
export const getUser = () => {
const userStr = sessionStorage.getItem('user');
if (userStr) return JSON.parse(userStr);
else return null;
}
// return the token from the session storage
export const getToken = () => {
return sessionStorage.getItem('token') || null;
}
// remove the token and user from the session storage
export const removeUserSession = () => {
sessionStorage.removeItem('token');
sessionStorage.removeItem('user');
}
// set the token and user from the session storage
export const setUserSession = (token, user) => {
sessionStorage.setItem('token', token);
sessionStorage.setItem('user', JSON.stringify(user));
}
You need the leading slash in your axios call, Change
axios.post("data/api/v1/login.json", {
username: username.value,
password: password.value,
})
to
axios.post("/data/api/v1/login.json", {
username: username.value,
password: password.value,
})
This will fix the problem with it adding the sub-path to your api call.

Resources