form submit re-renders page in react - reactjs

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

Related

ReactJs | Login requires refresh

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.

How to validate the form and send user to another page on successful data filled in form fields in React JS?

I want to send a user to Successful component of ReactJS after form with user and password has been filled up.
Here is the code:
import React from 'react';
import Successful from './Successful';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
user: '',
pass: ''
}
}
onsubmit = () => {
<Successful/>
}
render() {
return (
<form onSubmit={this.onsubmit}>
<div className="App">
<input type="text"
placeholder="Enter user"
validate="required"
onChange={() => this.state.user}
/>
<input type="password"
placeholder="Enter password"
validate="required"
onChange={() => this.state.password}
/>
<button type="submit">Submit</button>
</div>
</form>
)
}
}
export default App;
What I undestand you want to do is validate the password to see if it matches a user's password, if that happends, then redirect the user to another page? In that case, what you are doing good. Your validation logic should be located inside your onsubmit method, which triggers automatically when the user submits the form.
Inside that method you first, you should catch your form submit event, to prevent your page from reloading (which is the default behaviour when submitting a form). To do this, you receive the event object (which is passed automatically), and call it's preventDefault method:
onsubmit = (event) => {
event.preventDefault() // To prevent page reloading...
}
Here you check wheter the password entered is correct with a simple conditional, and then, there is the redirection part. I see you are just returning the component you wanna render (<Successful />). If what you wanna do is just show this component on the same page, you should add a new state property, that controls wheter the form input was successful or not, and then show or hide that component based on that state:
// Add the success prop
this.state = {
user: "",
pass: "",
success: false
}
onsubmit = (event) => {
event.preventDefault()
// Check if your password is valid...
// If its valid, then:
this.state.success = true
}
render() {
return (
<div>
... your other stuff here ...
{this.state.successful && <Successful />}
</div>
)
}
What {this.state.successful && <Successful />} does is that it only renders the component after &&, if the condition is true. Because of your validation process, this.state.successful is only true if the entered password passed the validation process, so your success component gets rendered only if the condition is fulfilled. The brackets in {this.state.... && <Successful />} are required.
If what you want to do is really redirect your user to another page, you should use something like react-router-dom (a library that allows you to do redirecting) and instead return its <Redirect /> component or browserHistory method. Its really too long to explain here, but you can check this or look for some tutorial on react-router because is an extensive library.
import React from 'react';
import Successful from './Successful';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
user: '',
pass: '',
success: false
}
}
onsubmit = () => {
this.state.setState({success:true})
}
const form = <form onSubmit={this.onsubmit}>
<div className="App">
<input type="text"
placeholder="Enter user"
validate="required"
onChange={() => this.state.user}
/>
<input type="password"
placeholder="Enter password"
validate="required"
onChange={() => this.state.password}
/>
<button type="submit">Submit</button>
</div>
</form>
render() {
return (
{!this.state.success ? form :
<Successful/>}
}
export default App;
Something like this could work. Here I added a "success" variable to the state. Initally set as false so the "form" is return and rendered.
onSubmit we set the state to true, this triggers a re-render and then we return the componant instead.
(I think I set the state correctly, I usually use hooks these days)
import { useHistory } from "react-router-dom"; //this goes on top
const history = useHistory();
onsubmit = () => {
//your logic for user input validation goes here
if(input is validated) {
history.push("/success");
}
else {
//show error here
}
}
Since you have added react-router in the tags. I am assuming that you have a route with success componenent.

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.

Refreshing issue when submitting form with react-redux-form

Issue
Important: This has been solved; see below explanation.
I've been having this issue of the page refreshing when submitting a react-redux-form. I have found a lot of similar issues since this is the default behavior of submitting a form in HTML; however I haven't found anything related to this specific library (react-redux-form).
I've also tried to apply what was suggested in other cases, mainly the event.preventDefault(), but I don't know how to use the event object with this library as they recommend the following syntax:
<LocalForm onSubmit={(values) => this.handleSubmit(values)}>
I've tried with values.event, but that was unsuccessful.
Below what I intend to have:
import React, { Component } from 'react';
import Hall from './HallComponent';
import { Row, Button, Label } from 'reactstrap';
import { Redirect } from 'react-router';
import { LocalForm, Control } from 'react-redux-form';
class Endgame extends Component {
constructor(props) {
super(props);
this.state = {
redirect: false
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(values) {
const date = new Date();
this.props.addScore({
date: date.toLocaleDateString("en-GB"),
name: values.name,
score: this.props.points
});
this.setState({ redirect: true });
}
render() {
if (this.state.redirect) {
return (
<Redirect to="/hall" />
);
}
else {
return(
<div className="container">
<div className="row">
<div className="col-12 col-md-5 m-1">
<p>Your score is: {this.props.points}</p>
<p>Add you name to the Hall of Fame</p>
<LocalForm onSubmit={(values) => this.handleSubmit(values)}>
<Row className="form-group">
<Label htmlFor="name">Nickname</Label>
<Control.text model=".name" id="name" name="name" placeholder="Your Name" className="form-control" />
</Row>
<Button type="submit">Submit</Button>
</LocalForm>
</div>
<div className="col-12 col-md-5 m-1">
<Hall scores={this.props.scores} />
</div>
</div>
</div>
);
}
}
}
export default Endgame;
Solution
I've found that the problem was not coming from the submit button but the way I had arranged my components.
I'm still not entirely sure of what actually happens behind the scene, but my Hall component above was unmounted and re-mounted each time the addScore() e.g. a redux action was fired. I found out that this could happen if the state of the parent component was modified somehow this could trigger re-mounting of children components.
I've migrated the state locally to Hall component connecting it to redux store and it now works properly.
its because you have a button of type "submit". Its default action is to refresh the page.
<Button type="submit">Submit</Button>
You can put an evt.preventDefault() in your handle submit function to stop the page from refreshing, but I would encourage you to just change the button type and put your handleSubmit function on the onClick event like this.
<Button type="button" onClick={this.handleSubmit}>Submit</Button>

Resources