React setState not setting state of input field - reactjs

I am using Gatsby. This is my code:
import React from "react"
class ContactCard extends React.Component {
constructor(props) {
super(props);
this.state = { form: { name: "test" }, message: ""};
this.handleChange = this.handleChange.bind(this);
}
handleSubmit = e => {
e.preventDefault();
};
handleChange = e => {
console.log("handleChange: " + e.target.name + " = " + e.target.value);
this.setState({ [e.target.name]: e.target.value, message: "event value: " + e.target.value });
/*
I also tried the following:
this.setState({ name: e.target.value, message: "event value: " + e.target.value });
*/
}
render() {
const { form: { name }, message } = this.state;
return (
<>
<p>{message} name: {name} </p>
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={name}
name="name"
onChange={this.handleChange}
/>
</form>
</>
);
}
}
export default ContactCard
When I type in the input box I see the following in the console log:
handleChange: name = testg
And the P tag changes to have the following text:
event value: testg name: test
The input box's value does not change it remains the same no matter what I do. As commented in my code, I also tried to setState on name directly instead of using the event name, that did not work. I also tried the following:
handleChange = e => {
console.log("handleChange: " + e.target.name + " = " + e.target.value);
var newState = { [e.target.name]: e.target.value, message: "event value: " + e.target.value };
this.setState(newState);
}
This results in the same behavior. What am I missing?

Issue
You are incorrectly setting state. Your state shape has name as a property of state.form, so the setState needs to match this shape.
Solution
Nest [e.target.name]: e.target.value in the form object. Ensure you shallow copy any existing form state as well. The React setState will shallow merge the root object, but it won't do a deep merge of nested state updates.
Since react state updates are asynchronous and (currently in react 16.x) react synthetic events are typically quickly nullified and returned to the event pool, you may also want to save the event name and value properties before enqueueing the state update.
handleChange = (e) => {
const { name, value } = e.target;
this.setState((prevState) => ({
form: {
...prevState.form,
[name]: value
},
message: "event value: " + value
}));
};

So you are updating the new state incorrectly.
You should be nesting name within the form property.
Sending from a mobile device, Pardon my formatting.

Can you try binding the value of input to this.state.form.name instead like so:
<input
type="text"
value={this.state.form.name}
name="name"
onChange={this.handleChange}
/>

Related

How to use multiple or two state values in one component or class file in react JS?

I am trying to use state to hide Semantic-UI Model form also to pass JSON to Customer class to add new customer. I am able to pass the value but in the end there is only one value.
When I start typing name, in console panel on 1st character name is empty "" and on 2nd character there is single "a"
In address
on create button click
when i click create button the name is empty"" and address has value.
import React from 'react';
import { Button, Form, Modal } from 'semantic-ui-react';
export default class AddCustomer extends React.Component {
constructor(props) {
super(props);
this.state = {
showCreateForm:false,
formData:{
name: '',
address: ''
}
}
this.handleChangeName = this.handleChangeName.bind(this);
this.handleChangeAddress = this.handleChangeAddress.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChangeName(event) {
const value = event.target.value;
console.log(value);
this.setState({formData:{name:value}});
//when i go to add input the formData is still empty name is empty.
//name: ""
//address: ""
console.log(this.state.formData);
}
handleChangeAddress(event) {
const value = event.target.value;
console.log(value);
this.setState({formData:{address:value}});
//name: "ram" but now there is no address in formData
console.log(this.state.formData);
}
handleSubmit(event) {
event.preventDefault();
////address: "aaaaa" now there no name in formData
console.log(this.state.formData);
this.setState({formData:{
name:this.state.name, address:this.state.address
}});
this.props.onAddFormSubmit(this.state.formData);
}
//On cancel button click close Create user form
closeCreateForm = () => {
this.setState({ showCreateForm: false })
}
//Open Create new Customer form
openCreateCustomer = () => {
this.setState({ showCreateForm: true })
}
render() {
return (
<div>
<Modal closeOnTriggerMouseLeave={false} trigger={
<Button color='blue' onClick={this.openCreateCustomer}>
New Customer
</Button>
} open={this.state.showCreateForm}>
<Modal.Header>
Create customer
</Modal.Header>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Form.Field>
<label>Name</label>
<input type="text" placeholder ='Name' name = "name"
value = {this.state.name}
onChange = {this.handleChangeName}/>
</Form.Field>
<Form.Field>
<label>Address</label>
<input type="text" placeholder ='Address' name = "address"
value = {this.state.address}
onChange = {this.handleChangeAddress}/>
</Form.Field>
<br/>
<Button type='submit' floated='right' color='green'>Create</Button>
<Button floated='right' onClick={this.closeCreateForm} color='black'>Cancel</Button>
<br/>
</Form>
</Modal.Content>
</Modal>
</div>
)
}
}
With these lines you are replacing your whole state with the value of one field:
Your initial state:
state = {
formData: {
name: "",
address: ""
}
}
Your state after this line:
this.setState({formData:{address:value}});
state = {
formData: {
address: ""
}
}
And with this line:
this.setState({formData:{name:value}});
state = {
formData: {
name: ""
}
}
As mentioned in the answer by Greg b, you have to change the value of the specific key and copy rest as they are. You can also use spread operator to copy rest of the unmodified fields easily.
this.setState({...this.state.formData, address: value})
will only change the address and copy rest of the state as it is. This comes handy when you have multiple fields in the state.
This is your state
formData:{
name: '',
address: ''
}
Change both these lines respectively
this.setState({formData:{address:value}});
this.setState({formData:{name:value}});
To
this.setState({formData:{name: this.state.formData.name, address:value}});`
this.setState({formData:{name:value, address: this.state.formData.address}});`
function handleChangeName(event) {
const value = event.target.value;
this.setState((prevState)=>({ formData: { name: value, address: prevState.formData.address }}),
()=>{ console.log(this.state.formData)});
}
function handleChangeAddress(event) {
const value = event.target.value;
this.setState((prevState)=>({ formData: { name: prevState.formData.name, address: value}}),
()=>{ console.log(this.state.formData)});
}
if you what to something next after setState() or state update, you have to use call back function.
State Updates are Merged
It looks like you aren't accessing the state correctly in the render(), I see you doing this.state.name and this.state.address rather than this.state.formData.nameand this.state.formData.address.
Also you probably shouldn't be storing your form data in a single state object, break it out into a field for each input, e.g.
this.state = {
showCreateForm: false,
name: "...",
address: "..."
}
You cannot update only part of an object in state, you need to update the whole thing. When you do this.setState({formData:{address:value}}); or are essentially setting this.state.formData.name to undefined, since the object is now going to only be { address: value }

The cursor backspace doesn’t remove last char of number format in react

I am new to react.
For e.g, input value entered 1,2,3,4 and after the event onChange it takes only numbers, then I can
remove 4,3,2 with backspace but not 1. And in HTML DOM also, the 1 cannot be removed.
class House extends Component {
state = {
room: null,
};
componentDidMount() {
if (this.props.house.rent) {
this.setState({ rent: this.props.house.rent });
}
}
onChange = (field, value, mutate) => {
if (field === "houseroom") {
value = parseInt(value.replace(/[#,]/g, ""));
}
mutate({
variables: {
},
});
this.setState({
[field]: value,
});
};
render(){
const {house} = this.props;
<SomeInput
type="text"
value={
(house.room&&
`$${house.room.toLocaleString("en")}`) ||
""
}
onChange={e => {
e.target.placeholder = "Room";
this.onChange("houseroom", e.target.value, mutate);
}}
}
/>
}
It look like a lot of a problem during the update of the state probably due to overrendering elsewhere in the component try to use prevState instead do ensure that state updating can not conflict.
this.setState(prevState => {
[field]:value;
});
Keep me in touch with the result.
Hope this helps someone !
Need to mention "this.state.room" in the input Value and check the prev state using the componentDidUpdate then "this.setState" here and also finally "this.setState" during the event change. Thanks all

onBlur function - setState incorrect

I am trying to run an unit test for the following code. I already test onClick and onChange and this.props
onBlur seems not to be working correctly based on the setState
Here is the method that onBlur is calling
handleChange = (e) => {
let localState = Object.assign({}, this.state)
localState[e.target.name] = e.target.value
let settingsObj = {
0: {
matchValue: e.target.name,
value: e.target.value,
transformType: 'replaceElement'
}
}
if(e.target.name === 'viewtitle'){
this.props.updateViewName(e.target.value);
}
this.props.updateViewXMLValue(settingsObj)
this.setState(localState)
}
Here is the onBlur event:
Title
</div>
<div className='tab-input-container'>
<input name='viewtitle' type='text' className="shape-dropdown" placeholder='Title' defaultValue={this.props.defaultData.viewtitle[0].length === 0 ? null : this.props.defaultData.viewtitle[0]} onBlur={this.handleChange}/>
</div>
Here is my test file:
it('Testing onBlur event on ViewTitle', () => {
baseProps.updateViewName.mockClear();
wrapper.setProps({
defaultData:{
viewtitle:[ [] ],
}
});
wrapper.setState({
localState: "blur"
});
wrapper.update()
wrapper.find('input[name="viewtitle"]').simulate('blur',
{
target: {
value:'blur-Test',
name:'viewtitle'
}
});
expect(baseProps.updateViewName).toHaveBeenCalled();
expect(wrapper.state('localState')).toEqual('blur-Test');
});
It seems that target value does not interfere
According to your code: localState[e.target.name] = e.target.value the onBlur handler sets the state objects viewtitle key to whatever the input is.
So if your original state was {localState: "blur"}, the new state would be {localState: "blur", viewtitle: "blur-test" }, which your test will fail as it is looking at the localState key.

Warning when changing controlled input value in React

I'm making a little blog in React and I have a problem updating the state on input change event.
The warning is:
Warning: A component is changing a controlled input of type text to be
uncontrolled. Input elements should not switch from controlled to
uncontrolled (or vice versa). Decide between using a controlled or
uncontrolled input element for the lifetime of the component
This is my code:
Constructor:
constructor(props){
super(props);
this.state = {
id: '',
post: {
title: '',
slug: '',
content: ''
}
}
this.handleChange = this.handleChange.bind(this);
}
handleChange function
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
post: {
[name]: value
}
});
}
input render:
render(){
return (
<div>
<AdminMenu />
<div id="admin-post-form">
<div className="input-group vertical">
<label>Título</label>
<input
name="title"
placeholder="Título"
type="text"
value={this.state.post.title}
onChange={this.handleChange}
/>
</div>
<div className="input-group vertical">
<label>Slug</label>
<input
name="slug"
placeholder="Slug"
type="text"
value={this.state.post.slug}
onChange={this.handleChange}
/>
</div>
</div>
</div>
)
}
What's wrong with my code ? The field is updated, but I get that warning.
Thanks!
This:
this.setState({
post: {
[name]: value
}
});
will replace this.state.post completely with an object that only has a single key. For example, if name is slug, you will replace post with { slug: 'something' }.
As a result, if you edit one field, all other fields will become undefined. React treats value={undefined} as an uncontrolled component and warns you.
To fix the issue, you probably want to merge post updates with the existing object instead of replacing it:
this.setState(prevState => ({
post: {
...prevState.post,
[name]: value
}
}));
Your set state is resetting the whole post object. You likely want to do something like:
this.setState({
post: {
...this.state.post
[name]: value
}
})
Solved using spread operator, this is the updated handleChange function that works with nested property:
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
var post = {...this.state.post}
post[name] = value;
this.setState({post});
}

React State is empty upon form submission

I have the following component that is submitting new posts. The form is using materiail-ui. I can successfully change the state. But when I submit the form, it gave me the error such that {state is empty} error. But on render event, I tried to console log the state, and I can see the state is changing whenever I type in the new values. Below is the sample of code. For the full codes you can check in my github , which is https://github.com/HtunHtunHtet/reactProject2/blob/master/client/src/components/add_post.js. Thanks in advance.
class AddPost extends Component {
state = {
postCategory: "react",
postTitle: "",
postAuthor: "",
postContent: ""
};
handleChange = (event, index, postCategory) => this.setState({postCategory});
handleSubmit(e){
e.preventDefault();
console.log(this.state);
const data = {
timestamp: Date.now(),
title: this.state.postTitle,
body: this.state.postContent,
author: this.state.postAuthor,
category: this.state.postCategory,
deleted: false,
voteScore: 1
};
this.props.fetchAddPost(data);
this.props.history.push("/");
}
handleInputChange = e => {
const target = e.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
};
Make handleSubmit a arrow function Or bind it to the component’s “this” in the constructor. Doing either of this will make sure that “this” refers to your Component, when you use “this” in handleSubmit.

Resources