I am using React JS.
Here is my React class:
class SomeClass extends React.Component{
constructor(props){
super(props);
this.state = {
passAccount: {
email: "Email"
},
errorMessage: ''
};
}
submitRequest = (event) =>{
//this.state.passAccount.email === 'Email' ? this.setState({errorMessage:'Please enter a valid email'}) : axios.post(`http://localhost:5000/otp/generate-passcode/${this.state.passAccount.email.toString()}`, this.state.passAccount)
axios.post(`http://localhost:5000/generate/${String(this.state.passAccount.email)}`)
.then((response) => {
let result = response.data;
}).catch((error) =>{
this.setState({errorMessage: ''});
});
console.log(`submitRequest email: `, this.state.passAccount.email);
}
handleChange = (event) =>{
console.log(`input detected`);
let request = this.state.passAccount;
let requestValue = event.target.value;
this.setState({passAccount: requestValue});
}
render() {
return (
<Form onSubmit={this.handleSubmit}>
<Form.Group>
<Form.Label>Email Address</Form.Label>
<Form.Control type="text" value={this.state.email} onChange={this.handleChange} placeholder="Enter Email Address" style={{width: '25rem'}}/>
</Form.Group>
<Button type="submit" onClick={() => this.submitRequest()}>Get OTP</Button>
<Button type="submit">Sign In</Button>
</Form>
);
}
}
export default SomeClass;
In Chrome console, this is what I am getting:
input detected
submitRequest email: Email //here is what I want to fix
Form Submitted Successfully
My question is:
In the line where it says in the console:
submitRequest email: Email //here is what I want to fix, for some reason the setState method is not working what is the reason behind that ?
Is the error in the handleChange method or in the submitRequest method ? what is a possible fix ?
Thanks.
When you this.setState({passAccount: requestValue}); you are setting passAccount to current value edited in form. But passAccount is an object with email property.
So I would suggest to modify your code in this way:
handleChange = (event) =>{
console.log(`input detected`);
let request = Object.assign({}, this.state.passAccount); // copy this.state.passAccount into request
request.email = event.target.value; // change request email
this.setState({ passAccount: request }); // set passAccount
}
You have declared your state variable passAccount as an object which contains an email property. If you want to update this email property then in your handleChange function, you need to update the state like this:
this.setState({ passAccount: { email: requestValue });
I am trying to create a todo list using React but i cant seem to understand why I am getting the error: "Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state."
Here's the code:
import React from 'react'
import ReactDOM from 'react-dom'
class Todo extends React.Component{
constructor(props){
super(props)
this.state = {
input: '',
list: []
}
this.handleChange = this.handleChange.bind(this)
this.reset = this.reset.bind(this)
this.removeItem = this.removeItem.bind(this)
this.add = this.add.bind(this)
}
add(){ //Adds a new task
const newItem = {
value: this.state.input,
id: Math.random + Math.random
};
const listed = [...this.state.list]
listed.push(newItem)
this.setState({
input: '',
list: listed
})
}
removeItem(id){ //deletes a task
const list = [...this.state.list]
const updatedList = list.filter(obj => {
return obj.id !== id
})
this.setState({
list: updatedList
})
}
handleChange(e){
this.setState({input: e.target.value})
}
reset(e){
e.preventDefault()
}
render(){
return (
<div>
<form action="" onSubmit={this.reset}>
<input type="text" value={this.state.input} placeholder='Enter a task..' onChange={this.handleChange} />
<button onClick={this.add}>Add Task</button>
{this.state.list.map(item => { //updates when a task is added or removed
return (
<div key={item.id}>
<h1>{item.value}</h1>
<button onClick={this.removeItem(item.id)}>X</button>
</div>
)
})}
</form>
</div>
)
}
}
ReactDOM.render(<Todo />,document.getElementById('root'))
Because you are calling removeItem on render. It needs to be wrapped in a separate function:
<button onClick={() => this.removeItem(item.id)}>X</button>
So that you only call it onClick and not on render.
<button onClick={this.removeItem(item.id)}>X</button>
In this button the event handler you have provided runs immediately due to the presents of the () at the end. To prevent this and still provide your argument item.id you can enclose the handler this.removeItem(item.id) with in another function.
I like the arrow function for this so mine looks like this
<button onClick={ ()=>this.removeItem(item.id) }>X</button>.
Math.random + Math.random is not returning a number like you would want for the element key. This is because your have neglected to include () at telling JS to run the function and return an int.
After making these changes, I ran it in codepen.io and it seemed to work fine.
I have created a basic form in react.js where I am able to get the values after the user submits the form.
However, when I try to change the values using the handleSubmit function, I don't see the changes made in the state.
I have made a copy of a state and changes are being reflected in the Copied State. But when I set the old state equal to the updated state, the changes are not reflected
My code is as follows
state = {
name: null,
ContactNumber: null
}
handleChange = (event) => {
this.setState({
[event.target.name] : event.target.value
})
}
handleSubmit = (event) => {
event.preventDefault()
let Copystate = JSON.parse(JSON.stringify(this.state))
Copystate.ContactNumber = 100
console.log(Copystate) // displaying the contact number as 100
this.setState({
state : Copystate
})
console.log(this.state) // displays the number which was submitted in the form
}
render(){
return(
<div>
<h2>Form</h2>
<form onSubmit={this.handleSubmit}>
<div>
<label>Name</label>
<input type="text" name="name" required = {true} onChange = {this.handleChange}/>
<label>Contact Number</label>
<input type="number" name="ContactNumber" required = {true} onChange = {this.handleChange}/>
<button type="submit" label="submit" >Submit</button>
</div>
</form>
</div>
);
}
}
Can anyone please let me know where I am going wrong? Thanks
Notice: setState is asynchronous: document state-updates-may-be-asynchronous
You can use a callback function to get the updated state
this.setState({state: Copystate}, () => {console.log(this.state)});
Or you can choose to use async/await
handleSubmit = async (event) => {
await this.setState({state: Copystate});
console.log(this.state);
}
Those two methods won't affect re-render since once the state is been updated, the re-render would proceed.
If you console in the render() you would find that it should always be updated finally.
render() {
console.log(this.state);
return (
...
)
}
setState is asynchronous.
So, you can do one of the following -
1. make a callback in setState to log the state or
2. write your console statement in the render function.
Why do you do this?
let Copystate = JSON.parse(JSON.stringify(this.state))
Copystate.ContactNumber = 100
You can change the handleSubmit to be like the following:
handleSubmit = (event) => {
event.preventDefault();
let { ContactNumber } = this.state;
ContactNumber = 100;
console.log(ContactNumber); // displaying the contact number as 100
this.setState({
ContactNumber: ContactNumber
}, () => {
console.log(this.state) // displays the number which was submitted in the form
})
}
please help me wit this code. I am struggling to update state right away after input is inserted. I was trying to do it with onSubmit method at least to sync input === submit after clicking on Button but still no luck as per console log.
See console picture:
enter image description here
How should I do it?
import React from 'react';
import './Search.css';
const results = ["Balaton", "Zamardi", "Sound", "Madarsko", "Sziget", "Hungary"]
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.onInput = this.onInput.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onInput(event) {
this.setState({
input: event.target.value,
submit: this.state.input});
console.log(this.state.input)
console.log(this.state.submit)
}
onSubmit(event) {
event.preventDefault()
if (results.includes(this.state.input)){
return alert("This is correct")
} else {
return alert("This not correct")
}
}
render() {
return(
<div>
<form className="search-form" onSubmit={this.onSubmit}>
<input type="text" value={this.state.input} placeholder="Our great memory" onChange={this.onInput}/>
<button type="submit">Try that!</button>
</form>
</div>
)
}
};
export default Search;
I re-wrote your component for readability, I believe you error is simply that setstate is async. This means that when you tried to set the state of submit at the same time as input, submit would always be one behind. By adding the callback in onInput after input has been set you should get the correct value ready to be submitted
import React, { Component } from 'react';
const results = ["Balaton", "Zamardi", "Sound", "Madarsko", "Sziget", "Hungary"]
class Search extends Component {
state = {
input: '',
submit: ''
};
// Added callback after input setstate
onInput = (event) => {
this.setState({
input: event.target.value}, () => this.setState({submit: this.state.input));
console.log(this.state.input)
console.log(this.state.submit)
}
onSubmit = (event) => {
event.preventDefault()
if (results.includes(this.state.input)){
return alert("This is correct")
} else {
return alert("This not correct")
}
}
render() {
return(
<div>
<form className="search-form" onSubmit={this.onSubmit}>
<input
type="text"
value={this.state.input}
placeholder="Our great memory"
onChange={this.onInput}/>
<button type="submit">Try that!</button>
</form>
</div>
)
}
};
export default Search;
onInput(event) {
this.setState({
input: event.target.value,
submit: event.target.value});,() =>
console.log(this.state.input)
console.log(this.state.submit)
}
This will let you see the correct values in log too.
Your submit state doesn't update as you assign the old value of this.state.input to it rather you should assign event.target.value.
In my React component I have a button meant to send some data over AJAX when clicked. I need to happen only the first time, i.e. to disable the button after its first use.
How I'm trying to do this:
var UploadArea = React.createClass({
getInitialState() {
return {
showUploadButton: true
};
},
disableUploadButton(callback) {
this.setState({ showUploadButton: false }, callback);
},
// This was simpler before I started trying everything I could think of
onClickUploadFile() {
if (!this.state.showUploadButton) {
return;
}
this.disableUploadButton(function() {
$.ajax({
[...]
});
});
},
render() {
var uploadButton;
if (this.state.showUploadButton) {
uploadButton = (
<button onClick={this.onClickUploadFile}>Send</button>
);
}
return (
<div>
{uploadButton}
</div>
);
}
});
What I think happens is the state variable showUploadButton not being updated right away, which the React docs says is expected.
How could I enforce the button to get disabled or go away altogether the instant it's being clicked?
The solution is to check the state immediately upon entry to the handler. React guarantees that setState inside interactive events (such as click) is flushed at browser event boundary. Ref: https://github.com/facebook/react/issues/11171#issuecomment-357945371
// In constructor
this.state = {
disabled : false
};
// Handler for on click
handleClick = (event) => {
if (this.state.disabled) {
return;
}
this.setState({disabled: true});
// Send
}
// In render
<button onClick={this.handleClick} disabled={this.state.disabled} ...>
{this.state.disabled ? 'Sending...' : 'Send'}
<button>
What you could do is make the button disabled after is clicked and leave it in the page (not clickable element).
To achieve this you have to add a ref to the button element
<button ref="btn" onClick={this.onClickUploadFile}>Send</button>
and then on the onClickUploadFile function disable the button
this.refs.btn.setAttribute("disabled", "disabled");
You can then style the disabled button accordingly to give some feedback to the user with
.btn:disabled{ /* styles go here */}
If needed make sure to reenable it with
this.refs.btn.removeAttribute("disabled");
Update: the preferred way of handling refs in React is with a function and not a string.
<button
ref={btn => { this.btn = btn; }}
onClick={this.onClickUploadFile}
>Send</button>
this.btn.setAttribute("disabled", "disabled");
this.btn.removeAttribute("disabled");
Update: Using react hooks
import {useRef} from 'react';
let btnRef = useRef();
const onBtnClick = e => {
if(btnRef.current){
btnRef.current.setAttribute("disabled", "disabled");
}
}
<button ref={btnRef} onClick={onBtnClick}>Send</button>
here is a small example using the code you provided
https://jsfiddle.net/69z2wepo/30824/
Tested as working one: http://codepen.io/zvona/pen/KVbVPQ
class UploadArea extends React.Component {
constructor(props) {
super(props)
this.state = {
isButtonDisabled: false
}
}
uploadFile() {
// first set the isButtonDisabled to true
this.setState({
isButtonDisabled: true
});
// then do your thing
}
render() {
return (
<button
type='submit'
onClick={() => this.uploadFile()}
disabled={this.state.isButtonDisabled}>
Upload
</button>
)
}
}
ReactDOM.render(<UploadArea />, document.body);
You can try using React Hooks to set the Component State.
import React, { useState } from 'react';
const Button = () => {
const [double, setDouble] = useState(false);
return (
<button
disabled={double}
onClick={() => {
// doSomething();
setDouble(true);
}}
/>
);
};
export default Button;
Make sure you are using ^16.7.0-alpha.x version or later of react and react-dom.
Hope this helps you!
If you want, just prevent to submit.
How about using lodash.js debounce
Grouping a sudden burst of events (like keystrokes) into a single one.
https://lodash.com/docs/4.17.11#debounce
<Button accessible={true}
onPress={_.debounce(async () => {
await this.props._selectUserTickets(this.props._accountId)
}, 1000)}
></Button>
If you disable the button during onClick, you basically get this. A clean way of doing this would be:
import React, { useState } from 'react';
import Button from '#material-ui/core/Button';
export default function CalmButton(props) {
const [executing, setExecuting] = useState(false);
const {
disabled,
onClick,
...otherProps
} = props;
const onRealClick = async (event) => {
setExecuting(true);
try {
await onClick();
} finally {
setExecuting(false);
}
};
return (
<Button
onClick={onRealClick}
disabled={executing || disabled}
{...otherProps}
/>
)
}
See it in action here: https://codesandbox.io/s/extended-button-that-disabled-itself-during-onclick-execution-mg6z8
We basically extend the Button component with the extra behaviour of being disabled during onClick execution. Steps to do this:
Create local state to capture if we are executing
Extract properties we tamper with (disabled, onClick)
Extend onClick operation with setting the execution state
Render the button with our overridden onClick, and extended disabled
NOTE: You should ensure that the original onClick operation is async aka it is returning a Promise.
By using event.target , you can disabled the clicked button.
Use arrow function when you create and call the function onClick. Don't forget to pass the event in parameter.
See my codePen
Here is the code:
class Buttons extends React.Component{
constructor(props){
super(props)
this.buttons = ['A','B','C','D']
}
disableOnclick = (e) =>{
e.target.disabled = true
}
render(){
return(
<div>
{this.buttons.map((btn,index) => (
<button type='button'
key={index}
onClick={(e)=>this.disableOnclick(e)}
>{btn}</button>
))}
</div>
)}
}
ReactDOM.render(<Buttons />, document.body);
const once = (f, g) => {
let done = false;
return (...args) => {
if (!done) {
done = true;
f(...args);
} else {
g(...args);
}
};
};
const exampleMethod = () => console.log("exampleMethod executed for the first time");
const errorMethod = () => console.log("exampleMethod can be executed only once")
let onlyOnce = once(exampleMethod, errorMethod);
onlyOnce();
onlyOnce();
output
exampleMethod executed for the first time
exampleMethod can be executed only once
You can get the element reference in the onClick callback and setAttribute from there, eg:
<Button
onClick={(e) => {
e.target.setAttribute("disabled", true);
this.handler();
}}
>
Submit
</Button>
Keep it simple and inline:
<button type="submit"
onClick={event => event.currentTarget.disabled = true}>
save
</button>
But! This will also disable the button, when the form calidation failed! So you will not be able to re-submit.
In this case a setter is better.
This fix this set the disabled in the onSubmit of the form:
// state variable if the form is currently submitting
const [submitting, setSubmitting] = useState(false);
// ...
return (
<form onSubmit={e => {
setSubmitting(true); // create a method to modify the element
}}>
<SubmitButton showLoading={submitting}>save</SubmitButton>
</form>
);
And the button would look like this:
import {ReactComponent as IconCog} from '../../img/icon/cog.svg';
import {useEffect, useRef} from "react";
export const SubmitButton = ({children, showLoading}) => {
const submitButton = useRef();
useEffect(() => {
if (showLoading) {
submitButton.current.disabled = true;
} else {
submitButton.current.removeAttribute("disabled");
}
}, [showLoading]);
return (
<button type="submit"
ref={submitButton}>
<main>
<span>{children}</span>
</main>
</button>
);
};
Another approach could be like so:
<button onClick={this.handleClick} disabled={isLoading ? "disabled" :""}>Send</button>
My approach is if event on processing do not execute anything.
class UploadArea extends React.Component {
constructor(props) {
super(props)
this.state = {
onProcess:false
}
}
uploadFile() {
if (!this.state.onProcess){
this.setState({
onProcess: true
});
// then do your thing
this.setState({
onProcess: false;
});
}
}
render() {
return (
<button
type='submit'
onClick={() => this.uploadFile()}>
Upload
</button>
)
}
}
ReactDOM.render(<UploadArea />, document.body);
Try with this code:
class Form extends React.Component {
constructor() {
this.state = {
disabled: false,
};
}
handleClick() {
this.setState({
disabled: true,
});
if (this.state.disabled) {
return;
}
setTimeout(() => this.setState({ disabled: false }), 2000);
}
render() {
return (
<button type="submit" onClick={() => this.handleClick()} disabled={this.state.disabled}>
Submit
</button>
);
}
}
ReactDOM.render(<Form />, document.getElementById('root'));