want to set value to an array key in react state - arrays

In the below code I have a state inside in emplist array with some keys and by onchange() event i want to set the value to emplist array keys. How can i set that values please help me :).
import React from 'react';
export default class Registrations extends React.Component{
constructor(props){
super(props);
this.state = {
EmployeeList:[
{ EmployeeId : '' },
{ EmployeeName :'' },
{ EmployeeSalary :'' },
{ EmployeeAddress : '' },
{ EmployeeDetails :false },
],
};
}
handleChange = (e) =>{
// some code actions//
}
render(){
var {EmployeeId,EmployeeName,EmployeeSalary,EmployeeAddress} = this.state;
return(
<div>
<form>
<input type="text" placeholder="Enter EmployeeId" onChange={this.handleChange}
value={EmployeeId} name='EmployeeId'/><br />
<input type="text" placeholder="Enter EmployeeName" onChange={this.handleChange}
value={EmployeeName} name='EmployeeName'/><br />
<input type="text" placeholder="Enter EmployeeSalary" onChange=
{this.handleChange} value={EmployeeSalary} name='EmployeeSalary'/><br />
<input type="text" placeholder="Enter EmployeeAddress" onChange=
{this.handleChange} value={EmployeeAddress} name='EmployeeAddress'/><br />
<button onClick={this.handleSubmit}>Submit</button>
</form>
</div>
);
}
}

Can you explain more about what you want to do.
In your code above, you type in the data for each piece of data inside a single employee object, so you state should has this shape:
this.state = {
Employee: {
EmployeeId: '',
EmployeeName: '',
EmployeeSalary: '',
EmployeeAddress: '',
EmployeeDetails: false
}
};
handleChange = (e) => {
this.setState({
Employee: {...this.state.Employee, [e.target.name]: e.target.value }
})
};
render() {
var {
EmployeeId,
EmployeeName,
EmployeeSalary,
EmployeeAddress,
} = this.state.Employee; // change the destructuring as well
}

Related

Basic Form react

Hello I just want to make a form and my textboxes does not take my inputs and my submit works but sends no values. What am I doing wrong? I know it's a basic question but I don't know what the problem is in my code.
Key problems:
it doesn't update the state nor take my inputs.
fields editable but cant write into them
Code
import React, { Component } from "react";
class Postform extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
category: "",
price: "",
};
}
changeHandler = (e) => {
this.setState = { [e.target.name]: e.target.value };
};
submitHandler = (e) => {
e.preventDefault();
console.log(this.state);
};
render() {
const { name, category, price } = this.state;
return (
<div>
<form onSubmit={this.submitHandler}>
<div>
<input
type="text"
name="name"
placeholder="Name"
value={name}
onChange={this.changeHandler}
/>
<input
type="text"
name="category"
placeholder="Category"
value={category}
onChange={this.changeHandler}
/>
<input
type="text"
name="price"
placeholder="Price"
value={price}
onChange={this.changeHandler}
/>
</div>
<button type="submit">Add product</button>
</form>
</div>
);
}
}
export default Postform;
Change this line to
changeHandler = e => {
this.setState({[e.target.name]: e.target.value });
};
Since setState is a function it is not a property !

How to push to an object to an array which is a state object, react way with Typescript

I need to maintain array of objects which is stored in a state object. Basically I need to push each object to this array whenever I click on Add button .This should basically store this object in array.
Also I am unable to fetch proper values when I am trying to submit?
Where am I going wrong?
Basically the structure I want is:
users= [
{"name":"xxx","email":"yyy","phone":"656"},
{"name":"yyy","email":"xxx","phone":"55"}
];
import * as React from 'react';
interface IState{
users : Account[];
}
interface Account{
name: string;
email: string;
phone: string
}
export default class App extends React.Component<{},IState> {
constructor(props:any){
super(props);
this.state= {
users: []
}
}
handleChange = ( event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({
users:{
...this.state.users,
[event.target.name]:event.target.value
}
})
}
onAdd = () => {
this.setState((prevState) => ({
users: [...prevState.users],
}));
console.log(this.state.users); // Unable to get the proper info
}
render(){
<React.Fragment>
<form onSubmit={this.onAdd}>
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"name"} />
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"email"} />
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"phone"} />
<button type="submit">Add</button>
</form>
</React.Fragment>
}
}
Update onAdd like below, because setState method updates state asynchronously, therefore you can't get the state right after calling it, but you can do so using a callback as second argument in setState method which is invoked after the method has updated the state
onAdd = () => {
this.setState((prevState) => {
const newUser = {}
return {
users: [...prevState.users, newUser],
}
}, () => {
console.log(this.state.users)
});
}
Things to correct:-
1) You are using only one state variable users for one user as well as all users. So create two state variables, one for temporary storing of data for a user and users variable for storing all users data.
2) You are trying to access console.log(this.state.users); after setState but it is not in the callback, setState is asynchronous it should be in callback of setState.
3) When user submits the form, the page refreshes which is default behaviour of application, we need e.preventDefault(); to override this behaviour.
4) Use state for individual input textboxes so that you could may be apply validation etc on fields.
import * as React from "react";
import { render } from "react-dom";
interface IState {
users : Account[],
user: Account
}
interface Account{
name: string;
email: string;
phone: string
}
class App extends React.Component<{}, IState> {
constructor(props: any) {
super(props);
this.state = {
users: [],
user: {name: '', email:'', phone: ''}
}
}
handleChange = (event: React.FormEvent<HTMLInputElement>) => {
this.setState({
user: {
...this.state.user,
[event.currentTarget.name]: event.currentTarget.value
}
});
};
onAdd = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
this.setState(
{
users: [...this.state.users, this.state.user],
user: {name:'', email: '', phone: ''}
},
() => {
console.log("updated state", this.state.users);
}
);
};
public render() {
const { name, email, phone } = this.state.user;
return (
<form onSubmit={this.onAdd}>
<input type="text" onChange={this.handleChange} value={name} name="name" />
<input type="text" onChange={this.handleChange} value={email} name="email" />
<input type="text" onChange={this.handleChange} value={phone} name="phone" />
<button type="submit">Add</button>
</form>
);
}
}
render(<App />, document.getElementById("root"));
Improvement Area - You could declare array of fields like fields = ['name', 'phone', 'email'] and map over in render function, this way you would need to write form once and any no of fields could be added.
Hope that helps!!!
you don't need handleChange, in onAdd get all inputs value from the event, put them in object( like {name: event.target.form.elements.name.value ...} and set it in users(which will be an array)
export default class App extends React.Component{
constructor(props:any){
super(props);
this.state = {
users: []
}
}
onAdd = (event) => {
const user = {
name: event.target.form.elements.name.value,
email: event.target.form.elements.email.value,
phone: event.target.form.elements.phone.value
}
this.setState({
users: [...this.state.users, user]
});
}
render(){
<React.Fragment>
<form onSubmit={this.onAdd}>
<input type="text" name="name" />
<input type="text" name="email" />
<input type="text" name="phone" />
<button type="submit">Add</button>
</form>
</React.Fragment>
}
}
then if you log this.state.users you will get the stractue you need
users= [
{"name":"xxx","email":"yyy","phone":"656"},
{"name":"yyy","email":"xxx","phone":"55"}
];
export default class App extends React.Component<{},IState> {
state = {
users: [],
text: '',
}
handleChange = ( event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({
[e.target.name]: e.target.value
})
}
onAdd = () => this.setState({
users:[
...this.state.users,
{
e.target.name: e.target.value,
e.target.email: e.target.value,
e.target.phone: e.target.value
}
]
})
render(){
<React.Fragment>
<form onSubmit={this.onAdd}>
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"name"} />
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"email"} />
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"phone"} />
<button type="submit">Add</button>
</form>
</React.Fragment>
}
}
You are setting your users as an array and then you are converting it into an object with the handleChange. Try to see if this works

React, setting initial value to input element

I have a list of students on my page. When I click on some student, a form should appear with input element for all of student's properties. When form is submitted, student's properties should change. The problem is when I want to set initial value of input elements as a properties of a current selected student. The value of input elements is filled with properties of a current student, but when I try to type something in that input, it's value does not change.
Code of Parent component
class App extends Component {
constructor(props){
super(props);
this.state = {
students :listStudents(),
visible: false,
index: 0,
student: listStudents()[0]
};
this.onClick = this.onClick.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.onNewStudentSubmit = this.onNewStudentSubmit.bind(this);
}
onClick(index){
if(this.state.visible){
this.setState({
visible: false,
index: 0
});
}
else {
let student1 = this.state.students
.filter(s => s.index === index);
this.setState({
visible: true,
index: index,
student: student1[0]
});
}
}
onSubmit(student){
let list = this.state.students
.map(s => {
if(s.index === this.state.index)
return student;
return s;
});
this.setState({
students : list,
visible: false
});
}
render() {
return (
<div className="App">
<StudentsList students={this.state.students} onClick={this.onClick}/>
<EditStudentDetails student={this.state.student} visible={this.state.visible} onSubmit={this.onSubmit}/>
<CreateStudent onSubmit={this.onNewStudentSubmit}/>
</div>
);
}
}
export default App;
onClick function changes selected student and stores it in state.student.
onSubmit function is triggered when the student's editing form is submitted.
Render returns the list of all students and a editing component which can be visible or not. Below is code of my editing component.
import React from 'react';
class EditStudentDetails extends React.Component{
constructor(props){
super(props);
this.state = {
name: "",
surname: "",
index : "",
class : "",
visible: ""
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleNameChange = this.handleNameChange.bind(this);
this.handleSurnameChange = this.handleSurnameChange.bind(this);
this.handleClassChange = this.handleClassChange.bind(this);
this.handleIndexChange = this.handleIndexChange.bind(this);
}
handleSubmit(e){
const student = {
name: this.state.name,
surname: this.state.surname,
index : this.state.index,
class : this.state.class
};
this.props.onSubmit(student);
e.preventDefault();
}
handleNameChange(e){
const newName = e.target.value;
this.setState({
name: newName
});
}
handleSurnameChange(e){
const newName = e.target.value;
this.setState({
surname: newName
});
}
handleIndexChange(e){
const newIndex = e.target.value;
this.setState({
index: newIndex
});
}
handleClassChange(e){
const newClass = e.target.value;
this.setState({
class: newClass
});
}
render(){
const type = this.props.visible ? {display: 'block'} : {display: 'none'} ;
return(
<div style={type}>
<form className="form-group" onSubmit={this.handleSubmit}>
Име
<input className="Name" type="text" onChange={this.handleNameChange} value={this.props.student.name}/>
Презиме
<input className="Surname" type="text" onChange={this.handleSurnameChange} value={this.props.student.surname}/>
Индекс
<input className="Index" type="text" onChange={this.handleIndexChange} value={this.props.student.index}/>
Насока
<input className="Class" type="text" onChange={this.handleClassChange} value={this.props.student.class}/>
<input className="submit btn" type="submit" value="Промени"/>
</form>
</div>
);
}
}
export default EditStudentDetails;
I try to set initial value by setting each input's value to the appropriate prop. Then when something is changed in the input component state should update. But as I said, the value of each input does not change.
You are telling React to use the passed prop as the input value, and since the props are not changed, the value isn't either. Set the passed props to the component state and update the state when the input is changed.
If you want to shave of some bytes, you could also use a more general onChange method as in the example below, given you are able to set a name property on the input fields.
class EditStudentDetails extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
surname: "",
index: "",
class: "",
visible: "",
...this.props.student
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleSubmit(e) {
const student = {
name: this.state.name,
surname: this.state.surname,
index: this.state.index,
class: this.state.class
};
this.props.onSubmit(student);
e.preventDefault();
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
render() {
const type = this.props.visible ? { display: 'block' } : { display: 'none' };
return (
<div style={type}>
<form className="form-group" onSubmit={this.handleSubmit}>
Име
<input className="Name" name="name" type="text" onChange={this.handleChange} value={this.state.name} />
Презиме
<input className="Surname" name="surname" type="text" onChange={this.handleChange} value={this.state.surname} />
Индекс
<input className="Index" name="index" type="text" onChange={this.handleChange} value={this.state.index} />
Насока
<input className="Class" name="class" type="text" onChange={this.handleChange} value={this.state.class} />
<input className="submit btn" type="submit" value="Промени" />
</form>
</div>
);
}
}
The problem is here.
<input className="Name" type="text" onChange={this.handleNameChange} value={this.props.student.name}/>
The value of the input is this.props.student.name,but in the this.handleNameChange, you change this.state.name,so when you type something,you can't see the change in the input value.
I suppose you need something like this:
this.state = {
class: props.student.name
}
...
<input className="Name" type="text" onChange={this.handleNameChange} value={this.state.name}/>

More compact way to write onChange handler for form with many inputs that affect nested state properties?

What is a more compact and more easy way to write a handleChange function for multiple input than the following:
import React from "react";
function initialState() {
return ({
customer: {
name: '',
primaryContactPerson: {
name: '',
email: ''
}
}
})
}
export default class AddCustomerForm extends React.Component {
constructor(props) {
super(props);
this.state = initialState();
this.handleChange = this.handleChange.bind(this);
}
handleChange(event, field) {
console.log(event.target.name);
switch(event.target.name) {
case 'customer.name':
console.log(1);
this.setState({
this: {
customer: {
name: event.target.value
}
}
})
break;
case 'customer.primaryContactPerson.name':
console.log(2);
this.setState({
this: {
customer: {
primaryContactPerson: {
name: event.target.value
}
}
}
})
break;
case 'customer.primaryContactPerson.email':
this.setState({
this: {
customer: {
primaryContactPerson: {
email: event.target.value
}
}
}
})
break;
default:
break;
}
this.setState({[event.target.name]: event.target.value});
}
render() {
return (
<div className='section section--full-width'>
<h1>Add Customer</h1>
<form onSubmit={this.handleSubmit}>
<div className='form__row'>
<label>
Customer name
</label>
<input type="text" name="customer.name" value={this.state.customer.Name} onChange={this.handleChange} />
</div>
<div className='form__row'>
<label>
Contact person
</label>
<input type="text" name="customer.primaryContactPerson.name" value={this.state.customer.primaryContactPerson.name} onChange={this.handleChange} />
</div>
<div className='form__row'>
<label>
Contact details
</label>
<input type="text" name="customer.primaryContactPerson.email" value={this.state.customer.primaryContactPerson.email} onChange={this.handleChange} />
</div>
<div>
<input type="submit" value="Add" />
</div>
</form>
</div>
);
}
}
I have tried the following method that does not work for nested objects:
handleChange(event, field) {
this.SetState({event.targetname: event.target.value})
}
One dirty hack is using lodash _.set Link. This is not the recommended method only a workaround.
you can use
const tmp = this.state;
_.set(tmp, event.target.name, event.target.value);
this.setState({ customer: tmp.customer });
if you want to avoid direct state mutation you can also use
const tmp = _.cloneDeep(this.state)
this process will be slower.

Handling multiple onChange callbacks in a React component

This is how I'm currently handling the scenario with two input boxes. As a separate update method for each one. Can/should this be done with a single handleChange method instead?
https://codepen.io/r11na/pen/bZKOpj?editors=0011
class App extends React.Component {
constructor(props) {
super(props);
this.handleChange1 = this.handleChange1.bind(this);
this.handleChange2 = this.handleChange2.bind(this);
this.state = {
name1: '',
name2: ''
};
};
handleChange1(e) {
this.setState({
name1: e.target.value
});
};
handleChange2(e) {
this.setState({
name2: e.target.value
});
};
render() {
return (
<div class="row column">
<Label name={this.state.name1}/>
<Input onChange={this.handleChange1} />
<Label name={this.state.name2}/>
<Input onChange={this.handleChange2} />
</div>
);
};
}
const Label = props => (
<p {...props}>Hello: <span className="label-name">{props.name}</span></p>
);
const Input = props => (
<input placeholder="Enter your name" {...props} type="text" />
);
ReactDOM.render(<App />, document.getElementById('app'))
Can/should this be done with a single handleChange method instead?
You can simplify your code like so.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name1: '',
name2: ''
};
};
handleChange(e, name) {
this.setState({ [name]: e.target.value });
};
render() {
return (
<div class="row column">
<Label name={this.state.name1}/>
<Input onChange={ (e) => this.handleChange(e, 'name1') } />
<Label name={this.state.name2}/>
<Input onChange={ (e) => this.handleChange(e, 'name2') } />
</div>
);
};
}
Example
Thanks #Alik that mentioned about eslint rule jsx-no-bind,
A bind call or arrow function in a JSX prop will create a brand new
function on every single render. This is bad for performance, as it
will result in the garbage collector being invoked way more than is
necessary.
Following this rule you can change your code like
class App extends React.Component {
constructor(props) {
super(props);
this.onChange = {
name1: this.handleChange.bind(this, 'name1'),
name2: this.handleChange.bind(this, 'name2'),
}
this.state = {
name1: '',
name2: ''
};
};
handleChange(name, event) {
this.setState({ [name]: event.target.value });
};
render() {
return (
<div class="row column">
<Label name={this.state.name1}/>
<Input onChange={ this.onChange.name1 } />
<Label name={this.state.name2}/>
<Input onChange={ this.onChange.name2 } />
</div>
);
};
}
Example

Resources