Invoke `componentDidUpdate` on form submit - javascript

I have a class component as follows:
class App extends Component {
constructor(props){
super(props);
this.state = {
abc: '',
someQuery: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentDidUpdate(){
fetch(`/someLink/${this.state.abc}`)
.then(response => {
return response.json();
}).then(data => {
this.setState({
someQuery: data.xxx
});
});
}
handleSubmit(e){
const target = e.target;
const value = target.value;
this.setState({
abc: value
})
e.preventDefault();
};
handleChange(e){
const target = e.target;
const value = target.value;
this.setState({
abc: value
});
};
render(){
return(
<form onSubmit={this.handleSubmit}>
<input name='abc' value={this.state.abc} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
<div>{this.state.abc} is currently accessing data from {this.state.someQuery}</div>
)
}
}
How do I run componentDidUpdate() every time I update the value of an input field and clicking the submit button?
The above invokes the life-cycle but due to the setState within handleChange() too, the life-cycle is invoked the moment I type something and doesn't wait till the submit button is clicked.
Removing the setState from handleChange() makes the input field value not editable anymore (cant type on the input field).
I need the input field value appended to the api link in the life-cycle but I can't seem to figure out the right way to do this.

You can add any method in component class and call it on submit. componentDidUpdate is not right place to do such thing especially setting state is crime :D
class App extends Component {
constructor(props){
super(props);
this.state = {
abc: '',
someQuery: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
doSommething (value){
fetch(`/someLink/${value}`)
.then(response => {
return response.json();
}).then(data => {
this.setState({
someQuery: data.xxx
});
});
}
handleSubmit(e){
const target = e.target;
const value = target.value;
this.setState({
abc: value
})
e.preventDefault();
doSommething(value);
};
handleChange(e){
const target = e.target;
const value = target.value;
this.setState({
abc: value
});
};
render(){
return(
<form onSubmit={this.handleSubmit}>
<input name='abc' value={this.state.abc} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
<div>{this.state.abc} is currently accessing data from {this.state.someQuery}</div>
)
}
}

Related

setState is ignored in handleSubmit

When the form submitted value should be 2.
this.state = { dog: 1 };
this.handleSubmit = this.handleSubmit.bind(this);
handleSubmit(e) {e.preventDefault(); this.setState({dog: 2})}
<Form onSubmit={this.handleSubmit}><Button variant="primary"
type="submit">Submit</Button></Form>
First of all, you should pass props in super()
constructor(props) {
super(props);
this.state = { dog: 1 };
}
Arrow function would solve the issue of this
handleSubmit = (event) => {
event.preventDefault()
this.setState({dog: 2})
}
<Form onSubmit={this.handleSubmit}>
<Button variant="primary" type="submit">Submit</Button>
</Form>
The setting state is asynchronous hence you might not be able to see the changed state just after setting it.
handleSubmit(e) {
e.preventDefault()
this.setState({dog: 2}, ()=>console.log(this.state.dog))
}
It will console 2.
This will help understanding of how setState occurs:
constructor(props) {
super();
this.state = {
dog: 1
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault()
this.setState({ dog: 2 }, () => {
console.log(this.state.dog)
})
}
render() {
const { dog } = this.state;
return (
<>
<span>Dog Value : {dog}</span>
<form onSubmit={this.handleSubmit}>
<button variant="primary" type="submit">Submit</button>
</form>
</>
)
}
class App extends Component {
constructor(props) {
super();
this.state = { dog: 1 };
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
this.setState({ dog: 2 });
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<button> Submit</button>
<p>{this.state.dog}</p>
</form>
);
}
}
This works just fine. What is Form ? Did you mean form?
Using your code you could try adding handleSubmit to the button's onClick to see if your state updates.

Submit button takes 2 clicks to generate output

I am trying to update state in react only after form is submitted. I have one html form which has 1 text input and a submit button, but it takes 2 click of submit button to actually change the state in react. I am using 2 methods handleSubmit and handleChange.
handleChange look for changes in input field and update the state accordingly.
handleSubmit append the state updated by handleChange to array on form submission
and state contains { itemslist: [], currentitem: "" }
when 1st time submit button is clicked it gives previous value of item (or gives empty array) and at 2nd time it gives array with value present in input field.
below is my full code
import React from 'react';
class App extends React.Component{
constructor(){
super()
this.state = {
currentitem: '',
itemslist: []
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit(event){
event.preventDefault();
this.setState((prevState) => {
return{
itemslist: prevState.itemslist.concat([this.state.currentitem])
}
})
console.log(this.state.items)
}
handleChange(event){
const {name, value} = event.target
this.setState({ [name] : value })
console.log(this.state.currentitem)
}
render(){
return(
<div>
<form onSubmit={this.handleSubmit} >
<input type='text' placeholder='enter text' name='currentitem' onChange={this.handleChange} value={this.state.currentitem} />
<button type='submit'>Submit</button>
</form>
</div>
)
}
}
export default App;
This answer could be a bit different of your code but this way it will work. Set the button type to button and make the button handle the submit, not the form. Then change the handleSubmit function to what I've got. I've tried it and it does works!:
import React from 'react';
class App extends React.Component{
constructor(){
super()
this.state = {
currentitem: '',
itemslist: []
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit(e){
e.preventDefault();
const { currentitem, itemslist } = this.state;
const newArray = [
...itemslist,
currentitem
];
this.setState({ itemslist, newArray });
}
handleChange(event){
const {name, value} = event.target
this.setState({ [name] : value })
console.log(this.state.currentitem)
}
render(){
return(
<div>
<form>
<input type='text' placeholder='enter text' name='currentitem' onChange={this.handleChange} value={this.state.currentitem} />
<button type='button' onClick={this.handleSubmit}>Submit</button>
</form>
// In cas eyou want to see the values in the array+
{
this.state.itemsList.map((item) => <p>{item}</>)
}
</div>
)
}
}
export default App;
setState function is asynchronous in react, so you cannot get the updated value immediately. But if you need to get the recent updated value from state, you must use callback function of setState.
this.setState({items: newItems}, () => { console.log(); })
I have modified your example like below to fulfil your requirement.
import React from 'react';
class App extends React.Component {
constructor() {
super();
this.state = {
currentitem: '',
items: []
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
this.setState((prevState) => {
return {
items: prevState.items.concat([this.state.currentitem])
}
}, () => {
console.log(this.state.items)
});
}
handleChange(event) {
const {name, value} = event.target;
this.setState({[name]: value});
console.log(this.state.currentitem);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type='text' placeholder='enter text' name='currentitem' onChange={this.handleChange}
value={this.state.currentitem}/>
<button type='submit'>Submit</button>
</form>
</div>
)
}
}
export default App;

React Component State Being Reset to Default

I am trying to track a form in this.state with event handlers on the inputs, but for some reason this.state is being reset back to its default state when the event handler tries to update it. This is an example of what the Component looks like.
class ExampleReport extends React.Component {
constructor(props) {
super(props)
this.state = {
reportDetails: null,
report: this.props.report,
form: {}
}
this.textInputHandler = this.textInputHandler.bind(this)
}
textInputHandler(e) {
var reportForm = this.state.form;
var target = e.target;
var name = target.className;
var value = target.value;
reportForm[name] = value;
this.setState({ form: reportForm })
}
render(){
return(
<form>
<input className="example" type="text" onChange={(e) => {this.textInputHandler(e)}} />
</form>
)
}
}
Before textInputHandler is called, this.state has an object and array stored in it, but once setState is called they are reset back to the default in the constructor. On subsequent updates to the text input this.state.form persists but everything else is reset. How can I prevent this from happening?
UPDATE
After trying some of the solutions suggested below I went back and logged this.state at just about every possible point and found that it is reset even before setState() is being called in the input handler.
You forget about input value and update state by ref (make mutation).
class ExampleReport extends React.Component {
constructor(props) {
super(props)
this.state = {
reportDetails: null,
report: this.props.report,
form: { example: '', },
}
this.textInputHandler = this.textInputHandler.bind(this)
}
textInputHandler(e) {
const { target: { name, value } } = e;
this.setState(prevState => ({
...prevState,
form: {
...prevState.form,
[name]: value,
},
}));
}
render() {
const { form: { example } } = this.state;
return (
<form>
<input
className="example"
type="text"
name="example"
value={example}
onChange={this.textInputHandler}
/>
</form>
)
}
}
Once way to solve this would be like below, spreading reportForm in a new object
textInputHandler(e) {
var reportForm = this.state.form;
var target = e.target;
var name = target.className;
var value = target.value;
reportForm[name] = value;
this.setState({ form: { ...reportForm } }) // <----------------------
}
However you may want to use more declarative solution as provided by Kirill but without unnecessary state changes
textInputHandler(e) {
const { className: name, value } = e.target;
this.setState(prevState => { form: { ...prevState.form, [name]: value } }) // <----------------------
}
try changing your onChange to this
onChange={this.textInputHandler}
like how they have it in the react documentation
EX)
<input type="text" value={this.state.value} onChange={this.handleChange} />
then do the spreading stuff
don't add, e.preventDefault because that only works for submitting
Look at Krill answer he has everything you need on there
compare it to yours from the top, you have a lot of stuff missing

How can I test binding of JSX form input field with JavaScript variable?

following is the code that needs to be tested.
class Booking extends Component{
constructor(props) {
super(props);
this.state = {
formValue: {
customerName: ""
}
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
const { formValue } = this.state;
this.setState({
formValue: { ...formValue, [name]: value }
});
}
render(){
return {
<form>
<input name="customerName" value={this.state.formValue.customerName} onChange={this.handleChange} />
</form>
}
}
}
I want to write a test case that checks whenever i enter value in the input field the same should be assigned to customerName of formValue. if not the test case should fail.

State of React.js component is not updated when browser auto-completes username

I have the following component in React:
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {username: '', password: '', redirectToReferrer: false};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const value = event.target.value;
const name = event.target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
console.log('A name was submitted: ' + this.state.username);
Auth.authenticate(this.state.username, this.state.password, () => {
this.setState({ redirectToReferrer: true })
})
}
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }
const { redirectToReferrer } = this.state
if (redirectToReferrer) {
return (
<Redirect to={from}/>
)
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<form id='loginForm'>
<input type="text" name="username" onChange={this.handleChange} />
<input type="password" name="password" onChange={this.handleChange} />
<button onClick={this.handleSubmit}>Log in</button>
</form>
</div>
)
}
}
When I use the browser auto-complete feature (instead of typing 'admin' I type just 'a' and let browser to fill the rest) the component's state is not update and it submits incorrect value. When I type the username/password all by hand it works correctly. How can I fix this? It's pretty common use case...
It looks like that some browsers have a bug.
You can try to workaround it with Autofill polyfill:
A polyfill to fire a change event when the browser auto fills form fields

Categories