How do you update individual properties of objects in React component states? - javascript

I am creating a simple React app that allows the user to add contacts to a master list. My components state looks like this:
state = {
contact: {
fname: "",
lname: "",
address: "",
phone: "",
email: "",
id: ""
}
};
So far, I have been able to effectively add properties such as name, email, etc using values sent from inputs.
this.setState({
contact: {
...this.state.contact,
[e.target.name]: e.target.value // e is an event sent from an input
}
});
};
That's all fine and dandy, but I need each contact to have a unique ID. I have been trying to tack on the ID to the contact object before I send it up the component hierarchy.
const unId = new Date().getTime();
this.setState({
contact: {
...this.state.contact,
id: unId
}
});
This code is producing some strange issues and I'm not sure why. When I run it for the first time, the id is generated, but not assigned to the contact. The second time I run it, the id produced the first time is assigned to the second contact. In other words, the id property is updating the state later one cycle behind the time it should.
I'm not very familiar with synchronicity or anything in React, so I would really appreciate any help I could get.

Does this example help you? If not, can you comment what do you exactly want?
https://codesandbox.io/s/priceless-mccarthy-7i69e
import React, { Component } from "react";
class App extends Component {
state = {
contact: {
fname: "",
lname: "",
address: "",
phone: "",
email: "",
id: new Date().getTime()
}
};
handleInputChange = e => {
this.setState({
contact: {
...this.state.contact,
[e.target.name]: e.target.value
}
});
};
handleSubmit = e => {
console.log(this.state);
};
render() {
const { fname, lname, address, phone, email, id } = this.state.contact;
return (
<div>
<label>fname</label>
<input
type="text"
value={fname}
name="fname"
onChange={this.handleInputChange}
/>
<br />
<label>lname</label>
<input
type="text"
value={lname}
name="lname"
onChange={this.handleInputChange}
/>
<br />
<label>address</label>
<input
type="text"
value={address}
name="address"
onChange={this.handleInputChange}
/>
<br />
<label>phone</label>
<input
type="text"
value={phone}
name="phone"
onChange={this.handleInputChange}
/>
<br />
<label>email</label>
<input
type="text"
value={email}
name="email"
onChange={this.handleInputChange}
/>
<br />
<label>id</label>
<input
type="text"
value={id}
name="id"
onChange={this.handleInputChange}
/>
<button type="button" onClick={this.handleSubmit}>
Submit
</button>
<hr />
{
JSON.stringify(this.state)
}
</div>
);
}
}
export default App;

The ID isn't created until the next render because the component's state isn't initialized when it gets created. You need to initialize its state.
Either in a class constructor
constructor() {
super();
this.state = {
id: new Date().getTime()
}
}
or a state attribute
state = {
id: new Date().getTime()
}
Here is a working codepen example

Related

In React, state is not being updated properly when concatenating individual input state values

I have seven different input fields and updating the state with the entered value. After that, I am concatenating all the state values and updating the contractNum state but it is not being updated correctly. It is missing the first state (this.state.contact.sys) value. I am not sure how to get the right concatenated value. Any help is much appreciated.
export default class test extends Component {
state = {
contact: {
sys: '',
co: '',
lgr: '',
mgr: '',
sub: '',
serial: '',
num: ''
},
contractNum: ''
};
test = testValue => {
this.setState({
contractNum: testValue
});
};
handleChangeFor = propertyName => event => {
const { contact } = this.state;
const newContact = {
...contact,
[propertyName]: event.target.value
};
this.setState({ contact: newContact });
let newValue =
contact.sub +
contact.co +
contact.mgr +
contact.lgr +
contact.sub +
contact.serial +
contact.num;
this.test(newValue);
};
render() {
return (
<div className="wrapper">
<div className="container">
<form>
<input
type="text"
onChange={this.handleChangeFor('sys')}
value={this.state.contact.sys}
maxLength={2}
/>
<input
type="text"
onChange={this.handleChangeFor('co')}
value={this.state.contact.co}
maxLength={1}
/>
<input
type="text"
onChange={this.handleChangeFor('mgr')}
value={this.state.contact.mgr}
maxLength={1}
/>
<input
type="text"
onChange={this.handleChangeFor('lgr')}
value={this.state.contact.lgr}
maxLength={1}
/>
<input
type="text"
onChange={this.handleChangeFor('serial')}
value={this.state.contact.serial}
maxLength={6}
/>
<input
type="text"
onChange={this.handleChangeFor('num')}
value={this.state.contact.num}
maxLength={2}
/>
<input
type="text"
onChange={this.handleChangeFor('sub')}
value={this.state.contact.sub}
maxLength={1}
/>
</form>
</div>
</div>
);
}
}
You used contact.sub instead of contact.sys when setting newValue.

React. Transferring data from textarea to array JSON

I just started working with React and JSON and require some help. There is a textarea field in which a user enters some data. How to read row-wise the entered text as an array into a JSON variable of the request? Any assistance would be greatly appreciated.
The result I want is
{
id: 3,
name: 'Monika',
birthDay: '1999/01/01',
countryDTO: 'USA',
films: [
'Leon:The Professional',
'Star wars',
'Django Unchained',
],
} ```
My code:
import React from 'react';
import { Form, FormGroup, Label } from 'reactstrap';
import '../app.css';
export class EditActor extends React.Component {
state = {
id: '',
name: '',
birthDay: '',
countryDTO: '',
films: [],
}
componentDidMount() {
if (this.props.actor) {
const { name, birthDay, countryDTO, films } = this.props.actor
this.setState({ name, birthDay, countryDTO, films });
}
}
submitNew = e => {
alert("Actor added"),
e.preventDefault();
fetch('api/Actors', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: this.state.name,
birthDay: this.state.birthDay,
countryDTO: {
title: this.state.countryDTO
},
films: [{ title: this.state.films }]
})
})
.then(() => {
this.props.toggle();
})
.catch(err => console.log(err));
this.setState({
id: '',
name: '',
birthDay: '',
countryDTO: '',
films: ''
});
}
onChange = e => {
this.setState({ [e.target.name]: e.target.value })
}
render() {
return <div>
<table>
<tr>
<td colspan="2">
<h3> <b>Add actor</b></h3>
<FormGroup>
<Label for="id">Id: </Label>
<input type="text" name="id" onChange={this.onChange} value={this.state.id} /><p />
<Label for="name">Name:</Label>
<input type="text" name="name" onChange={this.onChange} value={this.state.name} /><p />
<Label for="birthDay">Birth day:</Label>
<input type="text" name="birthDay" onChange={this.onChange} value={this.state.birthDay} placeholder="1990/12/31" /><p />
<Label for="country">Country:</Label>
<input type="text" name="countryDTO" onChange={this.onChange} value={this.state.countryDTO} /><p />
<Label for="Films">Films:</Label>
<textarea name="films" value={this.state.films} onChange={this.onChange} /><p />
</FormGroup>
</td>
</tr>
<tr>
<td>
<Form onSubmit={this.submitNew}>
<button class="editButtn">Enter</button>
</Form>
</td>
</tr>
</table >
</div>;
}
}
export default EditActor;
If you change the below code it will work automatically.
State declaration
this.state = {
name: 'React',
films:["Palash","Kanti"]
};
Change in onechange function
onChange = e => {
console.log("values: ", e.target.value)
this.setState({ [e.target.name]: e.target.value.split(",") })
}
change in textarea
<textarea name="films" value={this.state.films.map(r=>r).join(",")} onChange={this.onChange} />
Code is here:
https://stackblitz.com/edit/react-3hrkme
You have to close textarea tag and the following code is :
<textarea name="films" value={this.state.films} onChange={this.onChange} >{this.state.films}</textarea>
My understanding of your problem is that you would like to have each line in the text area dynamically added as an entry in the films array. This can be achieved as follows:
import React, { Component } from "react";
export default class textAreaRowsInState extends Component {
constructor(props) {
super(props);
this.state = {
currentTextareaValue: "",
films: []
};
}
handleChange = e => {
const { films } = this.state;
const text = e.target.value;
if (e.key === "Enter") {
// Get last line of textarea and push into films array
const lastEl = text.split("\n").slice(-1)[0];
films.push(lastEl);
this.setState({ films });
} else {
this.setState({ currentTextareaValue: text });
}
};
render() {
const { currentTextareaValue } = this.state;
return (
<textarea
defaultValue={currentTextareaValue}
onKeyPress={this.handleChange}
/>
);
}
}
Keep in mind that this method is not perfect. For example, it will fail if you add a new line anywhere other than at the end of the textarea. You can view this solution in action here:
https://codesandbox.io/s/infallible-cdn-135du?fontsize=14&hidenavigation=1&theme=dark
change textarea() tag
to
<textarea name="films" value={this.state.films} onChange={this.onChange} >{this.state.films}</textarea>
You can use split() :
films: {this.state.films.split(",")}

What's the best way to get textbox values on button click in React?

Normally in HTML you do something like this:
<form>
<input type="text"/>
<input type="text"/>
<input type="submit"/>
</form>
I believe this is not the React way to do it.
Another way to do like i did in my app, is not the best way to do as well i believe.
Like this:
buttonclickRequest(){
var reasonn = document.getElementById("testControl").value;
}
<div>
<FormControl id="testControl"/>
<Button id="btnRequest" onClick={this.buttonclickRequest}/>
</div>
In other stackoverflow topics I saw examples like this:
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
place: '',
address: '',
email: '',
phoneNumber: ''
};
}
handleClick() {
//do something
}
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
})
}
<div>
<input type="text" onChange={e => this.handleChange(e)}/>
<button type="submit" onClick={this.handleClick}/>
</div>
But i have my questions at this point as well,
I don't know how to do this properly with multiple text inputs:
You can make multiple specific changehandlers which is inefficiƫnt,
You can make a changehandler with a switch to set the properties
Is it even efficient to do a handle change on the inputfields? Because I just want the inputfield values when the button is clicked..
This is the form I'm talking about.
So how to properly get the multiple input data with React, when the button is clicked?
Thanks for your help in advance!
I think first you should add name attribute to your input field and use the name to set the state and then use the state on handleClick:
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
place: '',
address: '',
email: '',
phoneNumber: ''
};
}
handleClick = () => {
//do something
console.log(this.state);
// should be something like this {
// firstName: '',
// lastName: '',
// place: '',
// address: '',
// email: '',
// phoneNumber: ''
//}
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
return(
<div>
<input type="text" name="firstName" onChange={this.handleChange}/>
<input type="text" name="lastName" onChange={this.handleChange}/>
<button type="submit" onClick={this.handleClick}/>
</div>
)
}
Note that the name should match the state key.
Assuming you may be looking for state values
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
place: '',
address: '',
email: '',
phoneNumber: ''
};
}
handleClick() {
console.log("State ==>", this.state);
}
setFirstName = (e) => {
this.setState({
firstName: e.target.value
})
}
setPhoneNumber = (e) => {
this.setState({
phoneNumber: e.target.value
})
}
render(){
return('
<div>
<label> First Name </label>
<input type="text" name="firstName" onChange={e => this.setFirstName(e)}/>
<label> Phone Number </label>
<input type="text" name="phoneNumber" onChange={e => this.setPhoneNumber(e)}/>
<button type="submit" onClick={this.handleClick}/>
</div>
')
}
and yes... you are right creating change handlers for each input its not efficient on your case, what you need is a react form that gives you the old and submit options,you cant use old form because it needs to update the page to retrieve the values.
I Suggest you to use Antd form witch gives you all in components, i even suggest you to use their Input components witch look very nice and handles pretty well.
Antd Design (User interface components for react) - Antd
some sample code.
give it a try !!! CodeSandbox

Performing state and array manipulation more than once by passing props only once

I am new to React and this thing is confusing me a lot. I have root component that has an array and I am passing functions ADD and DELETE as props to the child component ui_forms. In the child component, I am taking input parameters and pushing or deleting from array depending on the button pressed. However, I cannot seem to perform push/delete operation more than once because I guess I am injecting the child component only once in the root component.
Is there by any chance a way to perform push or delete as many times a user wants by sending props only once?
Thank you
App.js
import FormsMap from './form_map';
class App extends Component {
state = {
maps : [
{'regno' : 'Karan', 'id' : 1},
{regno : 'Sahil', 'id' : 2},
{'regno' : 'Rahul', id : 4},
{regno : 'Mohit', id : 5},
{regno : 'Kartik', id : 3}
]
};
function_as_props(list1) {
console.log(this.state.maps);
let ninja = [...this.state.maps];
console.log(ninja);
ninja.push({"regno" : list1, "id" : Math.random()*10});
this.setState({
maps : ninja
});
console.log(ninja);
function_as_props1(name) {
let t = this.state.maps.indexOf(name);
let x = this.state.maps.splice(t,1);
console.log(x);
this.setState({
maps : x
});
console.log(this.state.maps);
}
}
render() {
const p = this.state.maps.map(list => {
return(
<div key={list.id}> {list.regno} </div>
);
})
return(
<FormsMap transpose = {this.function_as_props.bind(this)} traverse ={this.function_as_props1.bind(this)} /> <br />
);
}
}
export default app;
form_map.js
import React, { Component } from 'react';
class FormsMap extends Component {
state = {
name : null,
age : null,
hobby : null
};
changes(e) {
this.setState({
[e.target.id] : e.target.value
});
}
handle = (e) => {
e.preventDefault();
console.log(this.state);
this.props.transpose(this.state.name);
}
dels = (e) => {
this.setState({
[e.target.id] : e.target.value
});
}
del_button(e) {
e.preventDefault();
//console.log(this.state.name);
this.props.traverse(this.state.name);
}
render() {
return(
<React.Fragment>
<form onSubmit={this.handle}> {/* After entering all info, Press Enter*/}
<label htmlFor="labels"> Name : </label>
<input type="text" id="name" placeholder="Your name goes here..." onChange={this.changes.bind(this)} />
<label htmlFor="labels"> Age : </label>
<input type="text" id="age" placeholder="Your age goes here..." onChange={this.changes.bind(this)} />
<label htmlFor="labels"> Hobby : </label>
<input type="text" id="hobby" placeholder="Your hobby goes here..." onChange={this.changes.bind(this)} /> <br /><br />
<input type="submit" value="SUBMIT" /><br /><br />
</form>
<input type="text" id="name" placeholder="Enter name to delete..." onChange={this.dels} /> <button onClick={this.del_button.bind(this)}> DELETE </button>
</React.Fragment>
);
}
}
export default FormsMap;
Try this
App.js
import React, { Component } from "react";
import FormsMap from "./components/FormsMap";
class App extends Component {
constructor(props) {
super(props);
this.state = {
maps: [
{ regno: "Karan", id: 1 },
{ regno: "Sahil", id: 2 },
{ regno: "Rahul", id: 4 },
{ regno: "Mohit", id: 5 },
{ regno: "Kartik", id: 3 }
]
};
}
function_as_props(list1) {
let ninja = this.state.maps.concat({
regno: list1,
id: Math.random() * 10
});
this.setState({ maps: ninja });
}
function_as_props1(name) {
let x = this.state.maps.filter(
list => list.regno.toLocaleLowerCase() !== name.toLocaleLowerCase()
);
this.setState({
maps: x
});
}
render() {
return (
<React.Fragment>
{this.state.maps.map(list => <div key={list.id}>{list.regno}</div>)}
<FormsMap
transpose={this.function_as_props.bind(this)}
traverse={this.function_as_props1.bind(this)}
/>
</React.Fragment>
);
}
}
export default App;
FormsMap.js
import React, { Component } from "react";
class FormsMap extends Component {
state = {
name: null,
age: null,
hobby: null
};
changes(e) {
this.setState({
[e.target.id]: e.target.value
});
}
handle = e => {
e.preventDefault();
this.props.transpose(this.state.name);
};
dels = e => {
this.setState({
[e.target.id]: e.target.value
});
};
del_button(e) {
e.preventDefault();
this.props.traverse(this.state.name);
}
render() {
return (
<React.Fragment>
<form onSubmit={this.handle}>
{" "}
{/* After entering all info, Press Enter*/}
<label htmlFor="labels"> Name : </label>
<input
type="text"
id="name"
placeholder="Your name goes here..."
onChange={this.changes.bind(this)}
/>
<label htmlFor="labels"> Age : </label>
<input
type="text"
id="age"
placeholder="Your age goes here..."
onChange={this.changes.bind(this)}
/>
<label htmlFor="labels"> Hobby : </label>
<input
type="text"
id="hobby"
placeholder="Your hobby goes here..."
onChange={this.changes.bind(this)}
/>{" "}
<br />
<br />
<input type="submit" value="SUBMIT" />
<br />
<br />
</form>
<input
type="text"
id="name"
placeholder="Enter name to delete..."
onChange={this.dels}
/>{" "}
<button onClick={this.del_button.bind(this)}> DELETE </button>
</React.Fragment>
);
}
}
export default FormsMap;
This is the demo: https://codesandbox.io/s/xl97xm6zpo

Updates nested value in React State

I am using React/Redux for my web-app and I have the Description Class where user can edit description. Props description and propertyTypes are coming from AJAX calls.
import React, { PropTypes } from 'react';
const defaultDescription = {
about_you: '',
benefits: '',
description: '',
headline: '',
no_of_guests: 0,
property_name: '',
property_type: {
id: 1,
name: 'Apartment',
},
};
class Description extends React.Component {
static propTypes = {
description: PropTypes.object,
propertyTypes: PropTypes.array,
};
constructor(props) {
super(props);
this.state = {
description: defaultDescription,
};
this.handleInputChange = this.handleInputChange.bind(this);
this.propertyTypeChanged = this.propertyTypeChanged.bind(this);
this.updateDescription = this.updateDescription.bind(this);
}
// componentWillMount() {
// if (!this.props.description) {
// this.setState({
// description: defaultDescription,
// });
// }
// }
componentWillReceiveProps(nextProps) {
if (nextProps && nextProps.description && nextProps.propertyTypes) {
const newDescription = nextProps.description.description ? merge(defaultDescription, nextProps.description) : merge(nextProps.description, defaultDescription);
this.setState({
description: newDescription,
});
}
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
description[name]: value // <---- I want to update my state here
});
}
updateDescription(event) {
event.preventDefault();
console.log(this.state);
}
render() {
return (
<form name="descriptionForm" onSubmit={this.updateDescription}>
<input name="no_of_guests" value={this.state.description.no_of_guests} onChange={this.handleInputChange} /><br />
<input name="property_name" value={this.state.description.property_name} floatingLabelText="Property Name" onChange={this.handleInputChange} /><br />
<input name="headline" value={this.state.description.headline} floatingLabelText="Headline" onChange={this.handleInputChange} /><br />
<input name="summary" value={this.state.description.summary} floatingLabelText="Summary" onChange={this.handleInputChange} /><br />
<input name="description" value={this.state.description.description} floatingLabelText="Description" onChange={this.handleInputChange} /><br />
<input name="about_you" value={this.state.description.about_you} floatingLabelText="About You" onChange={this.handleInputChange} /><br />
<input name="why" value={this.state.description.why} floatingLabelText="Why ?" onChange={this.handleInputChange} /><br />
<input name="benefits" value={this.state.description.benefits} floatingLabelText="Benefits" onChange={this.handleInputChange} /><br />
<button value="Save" type="submit" />
</form>
);
}
}
export default Description;
I want to update the form but whenever the onChange event is fired , I can not update the state, and Input field is not changed.
How do I handle this case.
Any help would be appreciated.
Thanks.
You could write the update like this:
const desc = Object.assign({}, this.state.description);
desc[name] = value;
this.setState({
description: desc
});
This is how I would update in your case
const newState = {...this.state.description};
newState[name] = value;
this.setState({
description: newState,
});
Input elements should not switch from controlled to uncontrolled (or vice versa) [[https://facebook.github.io/react/docs/forms.html#controlled-components]]

Categories