ReactJs | Login requires refresh - reactjs

My login page doesn't work and need a refresh to be logged in. input data is displayed in the console with 201 but I have to manually refresh the page to continue.
this is my login.js:
export default class Login extends Component {
state = {};
submitHandler = e => {
e.preventDefault()
const logingdata ={
email : this .Email,
password: this .Password
}
axios
.post('/api/UserLogin', logingdata)
.then(response => {
console.log(response); //to print response in console in developper tool
localStorage.setItem('login', response.data.token);
console.log(this.Email);{
const loginmail=logingdata.email
console.log(loginmail);
}
})
.catch(error => {
console.log(error => console.log(error.response.data))
})
}
render() {
return (
<div className="outer">
<div className="inner">
<form onSubmit={ this.submitHandler}>
<h3>Log in</h3>
//Login form comes here.
<button type="submit" className="btn btn-dark btn-lg btn-block" >Sign in</button>
<p className="forgot-password text-right">
Forgot <Link to= "/Forgotpassword" >password?</Link>
</p>
</form>
</div>
</div>
);
}
}
I can not figure out what the issue is. Can someone help me with this?

The code you are presenting is saving the token in the localStorage and probably other components are handling the redirection when you will have that information in the storage. You will need to redirect to a specific URL ( home page probably ) after localStorage.setItem('login', response.data.token); and that's it.

You are setting user data logged after post api response. but react don't see the changes on localstorage.
You need to make your components to react to local storage changes.
Use something like react-use-localstorage
then you can look if the user data changed and render the logged components, or send the user to a new route for example.

Related

React: reset password send email with reset password link to reset the password

I am trying to create a forgot password component which has an email field. The user will enter his email. Now, this component should send a password reset email to that entered email.
The user will then click on that email link in his mail client which will redirect the user to another password reset page where the user will enter his email, new-password, and confirm password.
how do I achieve this? So far, I have just able to create a forgot password page. Not sure, how to proceed.
Code for my forgot password:
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { Button, Form, Input, InputGroup, InputGroupAddon } from "reactstrap";
const ResetPassword = () => {
const [email, setEmail] = useState("");
const history = useHistory();
const validateForm = () => {
return email.length > 0;
};
const handleSubmit = (event) => {
event.preventDefault();
};
return (
<div className="password-reset">
<div className="password-reset-form-container">
<div className="content">
<Form className="password-reset-form">
<h3 className="form-title">Password Reset</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="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</InputGroup>
<div className="form-actions">
<Button
className="pull-left"
block
bssize="small"
type="submit"
onClick={() => history.push("/")}
>
Cancel
</Button>
<Button
className="pull-right"
block
bssize="small"
disabled={!validateForm()}
type="submit"
onSubmit={handleSubmit}
>
Reset
</Button>
</div>
</Form>
</div>
</div>
</div>
</div>
);
};
export default ResetPassword;
You need to have an API endpoint that takes care of handling the process of sending a mail to the user related to the email.
The backend will provide a frontend link like http://localhost:3000/resetPassword/token
The token should be generated in the backend which will be sent back again with the new user password
In order to implement this kind of logic, all you need is a mailing service, to send email to client's email address, and in it, you can supply the forgot password page page/Url for client to submit new password.
And there's a open source project that can auto send email and serve the purpose: go take a look at Nodemailer doc or their github repo
Youtuber Ben has a section of video specifically solves this problem: Fullstack React GraphQL TypeScript Tutorial
When the backend gives you frontend route something like: http://localhost:3000/resetPassword/resetToken add below line in your routes.
<Route exact path="/resetPassword/:resetToken" component={ResetPassword} />

form submit re-renders page in react

I have a form in a react component.
When I click the submit button, I call signin(). In signin(), an error occurs as I can see the output in the chrome console, but it flashes by so quickly, I can't read it. Then the page refreshes and the error message is gone from the console.
Why is my form re-rendering the page? And how can I suppress the re-rendering so that I can read the error?
Here is my component:
import React, { Component } from 'react';
import './login.scss';
import axios from '../axios/axiosInstance';
class Login extends Component {
constructor() {
super();
this.usernameRef = React.createRef();
this.passwordRef = React.createRef();
}
signin() {
axios.get('/auth', {headers: {
username: this.usernameRef.current.value,
password: this.passwordRef.current.value}}).then(response => {
console.log('response=', response);
}).catch(err => {
console.log('err=', err);
});
}
render() {
return (
<div className="login-container">
<form onSubmit={this.signin}>
<div className="flex-row-end">
<div className="form-element flex-column-end">
<input type="text"
placeholder="Username or email"
name="username"
ref={this.usernameRef}
required />
</div>
<div className="form-element flex-column-end">
<input type="password"
placeholder="Password"
name="password"
ref={this.passwordRef}
required />
</div>
<div className="login-submit">
<button className="submit-login-button" type="submit"><i className="fas fa-sign-in-alt"> </i>Sign In</button>
</div>
</div>
</form>
</div>
);
}
};
export default Login;
As you can see, in signin(), I am using axios to send a request to my backend with user credentials. On the backend, the logs show no record of receiving the request. Therefore, the error must be happening before the request is sent. But I need a way to suppress the re-rendering of the page so that I can see what the error message is.
Thank you very much.
Change the signature of signin to take the form submission event, you'll need to prevent the default action on it. This keeps the page from reloading.
signin(e) {
e.preventDefault();
...
The error tells you that reference from component is not define yet.
Because method is not bound to this when used as a function:
<form onSubmit={e => this.signin(e)}>
and then put e.preventDefault() inside signin(e) which prevent the blink after you submit the form.
If I'm not mistaken axios will make the http request asynchronous, so you might have to use the event.persist() if this is not the case, preventDefault() should work as they mentioned in the answers above, also it is recommended to call the api in the componentDidMount()

How to listen to localstorage value changes in react?

I want to show a button when user is logged.If user is not logged then I m not showing button.When user logged i will set local storage values.when i set local storage in login Component,Header component must listen to that event and show the button.I m using addEventListener for listening.But its not listening.
I don't know where to listen in header Component.
// HeaderComponent(header.js):
class HeaderComponent extends Component {
componentDidMount(){
if(typeof window!='undefined'){
console.log(localStorage.getItem("token"));
window.addEventListener("storage",function(e){
this.setState({ auth: true});
})
}
}
render() {
return (
<div className="header">
<div className="container">
<div className="header-content">
<img src={logo} alt="logo"></img>
<div className="nav-links" >
<ul >
<li>Home</li>
<li>About</li>
<li>Services</li>
<li><NavLink activeClassName="active" to="/upload" >Upload</NavLink></li>
<li><NavLink activeClassName="active" to="/signup"> Sign Up</NavLink></li>
{ this.state.auth? <li onClick={this.onLogout}>Logout</li> :null}
</ul>
</div>
</div>
</div>
</div>
);
}
}
//loginComponent(login.js)
class LoginComponent extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(event) {
const data = {
username: document.getElementById('name').value,
password: document.getElementById('password').value
}
axios.post(`http://localhost:4000/user/login`, data).then(res => {
this.props.history.push("/");
localStorage.setItem("token",res.data.token);
localStorage.setItem("auth",true);
}).catch(err => console.log(err));
}
render() {
return (
<section class="log-in">
<div class="card-col">
<form>
<h3>LOG IN</h3>
<div class="form-controls">
<input id="name" type="text" placeholder="username" class="input"></input>
</div>
<div class="form-controls">
<input id="password" type="password" placeholder="password" class="input"></input>
</div>
<button type="submit" onClick={this.onSubmit} class="button" >Log in</button>
</form>
</div>
</section>
)
}
}
The current answers are overlooking a really simple and secure option: window.dispatchEvent.
Where you set your localStorage item, if you dispatch an event at the same time then the eventListener in the same browser tab (no need to open another or mess with state) will also pick it up:
const handleLocalStorage = () => {
window.localStorage.setItem("isThisInLocalStorage", "true");
window.dispatchEvent(new Event("storage"));
};
window.addEventListener('storage', () => {
console.log("Change to local storage!");
// ...
})
EDIT:
Because this seems to be helpful, I'd also recommended checking out the useLocalStorage hook from the usehooks-ts team. You don't need to install it as a package; you can just copy the hook wholesale. This hook makes use of the solution I originally shared, but adds a whole lot more sophisticated logic to it.
Please take note of two things
storage event works only when the same application opened in two browser tabs (it is used to exchange info between different tabs of the same app). Storage event will not fire when both components shown on the same page.
When adding event listerner, you're passing function(), not array function. function() doe not capture this so you should explicitly bind(this) or change it to arrow function.
For example
window.addEventListener("storage",(function(e){
this.setState({ auth: true});
}).bind(this));
Or do with arrow function
window.addEventListener("storage",(e) => {
this.setState({ auth: true});
});
Here is simple example.
Be sure to open it in two tabs (the same link). Store value in one tab and see this value in another tab.
I found a really bad hack to accomplish this:
I have a Toolbar and a Login Component where the Toolbar component listens to changes in localStorage and displays the logged-in user name when the Login Component updates local storage if authentication is successful.
The Toolbar Component
(similar to the Header component in your case)
const [loggedInName, setLoggedInName] = useState(null);
useEffect(() => {
console.log("Toolbar hi from useEffect")
setLoggedInName(localStorage.getItem('name') || null)
window.addEventListener('storage', storageEventHandler, false);
}, []);
function storageEventHandler() {
console.log("hi from storageEventHandler")
setLoggedInName(localStorage.getItem('name') || null)
}
function testFunc() {
console.log("hi from test function")
storageEventHandler();
}
Add a hidden button to your Toolbar component. This hidden button will call the testFunc() function when clicked which will update the logged-in user's name as soon as local storage is updated.
<button style={{ display: 'none' }} onClick={testFunc} id="hiddenBtn">Hidden Button</button>
Now, in your Login component
.
.
.
//login was successful, update local storage
localStorage.setItem("name",someName)
//now click the hidden button using Javascript
document.getElementById("hiddenBtn").click();
.

How can I on Submit my Form redirect the Results to another Page in React

I am trying to get the search results to display on another page in React Js. For now, when I submit my search form, it will display the results on the same page which I don't want. I have been trying to do this for the past 4 hours (I am new to ReactJS) and I have been seeing a lot of suggestions online but nothing seems to work as I was hoping.
I have even tried to use react redux, react router and much more example online but does not work, don't what I'm doing wrong.
How can I approach it? I would please like someone to probably put me on the right track in regards to the code I provide below and also your suggestions/feedback are welcome.
Thank you in advance.
Here is my search engine component responsible for the form
const SearchEngine = (props) => {
return (
<div>
<h3 className="spacer">Search Engine</h3>
<form onSubmit={props.getTrend}>
<div class="input-group md-form form-sm form-2 pl-0">
<input class="form-control my-0 py-1 red-border" type="text" name="keyword" placeholder="Enter your keyword" aria-label="Search" />
<div class="input-group-append">
<button class="input-group-text red lighten-3" id="basic-text1"><i class="fa fa-search text-grey" aria-hidden="true"></i></button>
</div>
</div>
</form>
</div>
);
}
export default SearchEngine;
and here is the result component where I would like to display the results
const Results = (props) => (
<div>
Hello
</div>
);
export default Results;
After receiving your api calls you can throw a push and move to another page. For yours it may look something like this.
getTrend = async (e) => {
e.preventDefault();
const keyword = e.target.elements.keyword.value;
const api_call = await fetch(`http://localhost:8081/trend/twitter?keyword=${keyword}`); //make API call
const data = await api_call.json();
if (keyword) {
this.setState({
tweets: data
});
console.log(this.state.tweets);
this.props.history.push({
pathname: '/results',
state: { detail: data }
})
}
else {
this.setState({
tweets: undefined,
error: "Please enter the values"
});
}
}
Then, in your App.js you can access it with
props.location.state.detail
This may require that you use withRouter as a HOC. One thing I would change is grab your api in componentDidMount. However, it would be much easier if you went about this with hooks. While it may require some refractoring, you could make your api call with a hook, which is a ton simpler to get up and running.

React multiple conditions inside ternary operator?

I'm building an open forum and I got everything working the way I want however, I recently ran into a small bug in my code. While a lot of forums require a log in to view topics that are asked, mine is built without the user having to register or log in (I built it like this in case the user simply wants to view topics without having to register or log in). For one of the functionalities, I have it to where users can delete there replies for a specific question thread.
Therefore, I got it to where it only recognizes the users ID and it'll delete the reply based off of that. However, if the user is not logged in, they'll still be able to delete the reply (this is where the bug is).
So my question is it possible that I can check for 2 conditions inside my ternary operator?
I want to check 1st if the user is logged in, if they are, check if the userId matches the reply_user_id and if both cases pass, they'll be able to delete there reply. However, if one fails Don't display the trash icon. Right now, the trash icon works fine when logged in but it's displaying the trash icon if the user is not logged in.
I have a lot of code so I'm only going to show the portion relating to my question:
import React from 'react';
import Header from '../common/Header';
import { Link } from 'react-router-dom';
import { Modal, Button } from 'react-bootstrap';
import Pagination from "react-js-pagination";
import ForumpageService from '../../services/forumService';
import appController from '../../controllers/appController';
import { confirmAlert } from 'react-confirm-alert';
class Forumreplies extends React.Component {
constructor(props){
super(props);
this.deleteReply = this.deleteReply.bind(this);
this.pagination = this.pagination.bind(this);
this.state = {
topicId: 0,
replyId: 0,
userId: this.props.match.params.userid,
postDetails: {},
repliesData: [],
reply: '',
errorMsg: '',
isLoggedin: false,
canDelete: false,
}
}
async componentDidMount(){
// Check if user is logged in
if(localStorage.getItem('userData') !== null) {
this.setState({isLoggedin: true})
}
const topicId = this.props.match.params.topicid
const postDetails = await ForumpageService.replyDetails({topicId: topicId})
this.setState({
postDetails: postDetails[0],
topicId: topicId
})
await this.postReplies();
console.log(this.state);
}
}
deleteReply(id, e){
confirmAlert({
customUI: ({ onClose }) => {
return (
<div className='custom-ui'>
<h1>Are you sure</h1>
<p>You want to delete this reply?</p>
<button className="btn btn-primary" onClick={onClose}>Cancel</button>
<button className="btn btn-primary" onClick={() => {this.confirmDelete(id); onClose();}}>Confirm</button>
</div>
)
}
})
}
render(){
const repliesData = currentReply.map((row, index) => {
return (
<div className="reply-container" key={index}>
{row.reply_status == 0 ?
<div className="row" id="reply-messages-deleted">
<div className="col-md-8">
<p>{row.userName}</p>
<p>{row.reply_message}</p>
<p>This reply has been deleted</p>
</div>
<div className="col-md-2">
// Multiple condition I want to check, right now it's only checking 1 condition which is the userid to the reply id but I want to also check if the user is offline as well.
{this.state.userId == row.reply_user_id ? <i className="far fa-trash-alt" onClick={this.deleteReply.bind(this, row.reply_id)} title="Delete this reply?"></i> : null }
</div>
</div>
:
<div className="row" id="reply-messages" key={index}>
<div className="col-md-8">
<p>{row.userName}</p>
<p>{row.reply_message}</p>
</div>
<div className="col-md-2">
{this.state.userId == row.reply_user_id ? <i className="far fa-trash-alt" onClick={this.deleteReply.bind(this, row.reply_id)} title="Delete this reply?"></i> : null }
</div>
</div>
}
</div>
)
})
export default Forumreplies;

Resources