How to store an object in component's state? - reactjs

I make an Ajax request that returns me an object like this :
Object { user1: "darkblue", user2: "darkred" }
How can I store this object in the component's state since none of the following works :
this.state = { usersColors: '' } // use for strings
this.state = { usersColors: [] } // used for arrays
Which syntax use for storing objects?

To initialise any variable as object, write it like this:
this.state = { usersColors: {} }
And use setState to update the state once you get the response, like this:
this.setState({usersColors: response})
Now you can access the response values by:
this.state.usersColors.user1 ----> "darkblue"
this.state.usersColors.user2 ----> "darkred"
Update:
Check this snippet:
let state = {
userColors: {
user1: 'red',
user2: 'blue'
}
}
let username = 'user1';
console.log('user1 value = ', state.userColors[username]);

You should never use this.state = ... anywhere except in a class constructor. Instead use this.setState({usersColors: {myObj: foo, etc: bar}}) One caveat: don't use this.setState in the render() method, you will just get an error.

How to map the different html elements who are going to be the part of the same array object to the object in the components state. Like if i have following two html elements who are part of a single object user
<div className="form-group">
<input
className="form-control"
type="text"
name="user[username]"
value={this.state.user.username}
onChange={this.onChange}
placeholder="Customer Name"
/>
</div>
<div className="form-group">
<input
className="form-control"
type="text"
name="user[mobilenumber]"
value={this.state.user.mobilenumber}
onChange={this.onChange}
placeholder="Customer mbole no."
/>
</div>
What should i give the name to these two elements such that they get mapped to the user object in the state present in the constructor
this.state = {
user: { username: "", mobilenumber: "" },
clothname: "",
description: "",
errors: {}
};
ANd with the following simple onChange function
onChange(event) {
this.setState({ [event.target.name]: event.target.value });
}

Related

React Form single handleChange function with multiple inputs and setState to complex object

I have a form inside a React component that has several inputs, representing properties of an object, defined in the component's state. I would like to make a POST request to my REST API by which I want to create an entity, by passing the filled in object from this.state, which in turn is modified by the form inputs.
I am unable to figure out how to use single handleChange() method to update the state on an onChange event of all the fields. I refereed to articles describing this problem but without complex types. I even tried to implement the dynamic key name by using [event.target.name] and adding name attribute to all of my form inputs but with no result.
Here is my code.
Component constructor and state:
class IngredientForm extends React.Component {
constructor(props) {
super(props);
this.apiService = new apiService();
this.state = {
ingredient: {
id: "",
name: "",
dateAdded: "",
}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
}
handleChange and handleSubmit methods:
handleChange(event) {
const ingredient = this.state.ingredient;
ingredient[event.target.name] = event.target.value;
this.setState({ ingredient: ingredient });
}
handleSubmit(event) {
//Fetch code goes here
let response = this.apiService.postDataAsync("https://localhost:5001/api/ingredients",this.state.ingredient);
this.setState({ingredient:{id: response.id, dateAdded: response.dateAdded}});
event.preventDefault();
}
And this is the code of the form:
<form onSubmit={this.handleSubmit}>
<label>
Name
</label>
<input
type="text"
name="name"
value={this.state.ingredient.name}
onChange={this.handleChange}
required
/>
<label>Date added</label>
<input
type="date"
name="dateAdded"
value={this.state.ingredient.dateAdded}
onChange={this.handleChange}
/>
<button type="submit" value="Submit">
Save
</button>
</form>
I will be glad to receive your help. I tried to find a similar topic in the forum beforehand, but without success, everyone discussed the situation with primitive types in the state.
hey there the problem is in your set state can you tru this?
handleChange(event) {
const {name, value} = event.target;
this.setState({
ingredient: {
...this.state.ingredient,
[name]: value
}
});
}

ReactJS event handler update state of dictionary value

I am trying to write a event handle for few input box and I realize that it's not able to update the state of the dict. if I change it to string it works fine.
if I change state to following it works fine.
this.state = {
firstName: "",
lastName: ""
}
However following doesn't
import React, {Component} from "react"
class App extends Component {
constructor() {
super()
this.state = {
list: {
firstName: "",
lastName: ""
}
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
const {name, value} = event.target
console.log(name)
this.setState({
[name]: value
})
}
render() {
return (
<form>
<input
type="text"
value={this.state.firstName}
name="list[firstName]"
placeholder="First Name"
onChange={this.handleChange}
/>
<br />
<input
type="text"
value={this.state.lastName}
name="list[lastName]"
placeholder="Last Name"
onChange={this.handleChange}
/>
<h1>{this.state.firstName} {this.state.lastName}</h1>
</form>
)
}
}
export default App
First, you're correctly destructuring the name and value props from event.target in your handleChange function, BUT the name properties you set on your two <input> elements are not intuitive. Your name properties are currently "list[firstName]" and "list[lastName]" -> this won't reach into your this.state.list[firstName] / this.state.list[lastName] properties as you wish - instead, you should change your name properties to reflect your state values, like this:
<input
name="firstName"
{/* other props stay the same... */}
/>
<input
name="lastName"
{/* other props stay the same... */}
/>
Now that your <input> elements have name properties that also match values on your state, you can change your handleChange function to something like this:
handleChange(event) {
// get name and value properties from event target
const {name, value} = event.target
this.setState(prevState => ({
// update your 'list' property
list: {
// spread old values into this object so you don't lose any data
...prevState.list,
// update this field's value
[name]: value
}
}))
}
In your handleChange function you can change setState to following:
this.setState({
list: {
[name]: value
}
})
// in input
value={this.state.list.firstName}
The second case does not work because there are flaws. So to make your code run you need to make two changes in your input field
1) name="list[firstName]" as name="firstName"
2) value={this.state.firstName} as value={this.state.list.firstName}
If you use name="list[firstName]" in your input field then whenever [name]: value in handleChange method executes, it evaluates to ['list[firstName]']: value and it will create another property list[firstName] in the state.
i.e state = { list: {...}, list[firstName]: value }.
So it won't update the property firstName inside the list as you expect.
For more detail: Computed Property Names
And using value={this.state.list.firstName} we can map state list.firstName with the input field
<input
type="text"
// do not use value={this.state.firstName}
value={this.state.list.firstName}
// do not use name="list[firstName]"
name="firstName"
placeholder="First Name"
onChange={this.handleChange}
/>
<input
type="text"
value={this.state.list.lastName}
name="lastName"
placeholder="Last Name"
onChange={this.handleChange}
/>
In your handleChange method, your are trying to update the property firstName and lastName inside list.
So to do that first you need to use list inside this.setState method as this.setState({ list: {...}}).
As list is an object and you want to update specific property of list so first you need to copy all properties inside the list using spread operator. And then after that you can change the property you want to change using dynamic / computed property. So change your handleChange method to
handleChange(event) {
const {name, value} = event.target
this.setState({
list: {
// copy all properties inside the "list"
// so that we change only the property
// we need to change and keep other properties as it is
...this.state.list,
// dynamically changing property
[name]: value
}
})
}

React Form issue with JSON

I have a form with fields formatted like so
<Form.Field>
<input
type="text"
name="firstname"
placeholder="First Name"
value= { this.state.user.firstname }
onChange= { this.onChange }
/>
</Form.Field>
This works fine. My onChange event manages this perfectly.
state = {
user: {
firstname: "",
lastname: ""
}
}
onChange = e => {
this.setState({
user: { ...this.state.user, [e.target.name]: e.target.value }
});
}
Originally, I had attempted to have my JSON object returned from my API with a nested hierarchy around name.
state = {
user: {
name: {
firstname: "",
lastname: ""
}
}
}
But when it's like this, my onChange object adds variables to my state rather than managing the existing ones; even though events fire, I'm not changing the value on the UI. I attempted to navigate the JSON hierarchy by manipulating the name of the form.field to match the JSON but that didn't work:
<Form.Field>
<input
type="text"
name="name.firstname"
placeholder="First Name"
value= { this.state.user.name.firstname }
onChange= { this.onChange }
/>
</Form.Field>
What am I missing?
Your assumption is good, you can't navigate your JSON object in react using this syntax: { [e.target.name]: e.target.value }
The simplest solution is to use a different onChange function that modifies specifically your name sub-object.
If you still want to have a single function, it should be possible to parse the e.target.name value and split the string on . character, but it sounds odd to me.

Add Name to Array using setState gives error

Im trying to add an item to an array in my state. However, i get the following error:
Objects are not valid as a React child (found: object with keys...)
My github url to the codes, Please help.
class Tutorial2Screen extends Component {
constructor(props) {
super(props);
this.state = {
num: 0,
name: "",
arr: ["John", "Mary", "Steve"]
};
}
addNameToArr(newName) {
this.setState(prevState => ({
arr: prevState.arr.concat(newName)
}));
}
Input & Button codes:
<Input
placeholder="Name"
onChangeText={name => this.setState({ name })}
value={this.state.name}
/>
</Item>
<Button
block
style={{ margin: 10 }}
onPress={this.addNameToArr.bind(this)}
>
you have binded "addNameToArr" method with Button on press event, and in "addNameToArr" method, first argument you are expecting a "newName" (input field value) value to be passed in. But, as method is bind with an event call, so you are not getting text input value as argument.
Use:
addNameToArr() {
this.setState(prevState => ({
arr: prevState.arr.concat(prevState.name)
}));
You have to do it like this :
this.setState({
arr:this.state.arr.concat(this.state.name);
})
or you can use it like this by es6 spread syntax :
this.setState({
arr:[...this.state.arr,this.state.name];
})
changed newName to this.state.name because you are changing this.state.name with onchange handler not newName
thanks for all your help! your inputs have helped me find the solution to my question. I have altered my jsx code by adding in brackets and passing the argument directly to the arrow function and it worked! github contains the code that works.

React: why binding value makes input readonly?

given this initial state:
this.state = {
person: { id: 1, name: "Liero" }
};
when I bind input like this: <input value={this.state.person.name} />
Then I makes the input non editable. I understand that it would make state and DOM element out of sync.
However, when I use defaultValue: <input defaultValue={this.state.person.name} />,
I loose posibility to change the person's name from code (e.g. when I want to reset the form).
When I manually sync the state and input value:
<input value={this.state.person.name}
onChange={(evt) => this.state.person.name = evt.target.value }/>
it does not work until I call setState, or force refresh?
Do I really need to force render of entire component each time the input value changes, when I want to have the posibility to control the person.name from code?
Instead of set this.state.person.name directly. call this.setState.
This will trigger another render cycle and then bind this.state.person.name to value:
<input value={this.state.person.name}
onChange={(evt) => {
this.state.person.name = env.target.value;
this.setState({person:this.state.person});
}}/>
Did you mean to setState() instead of mutate the state? like this:
class MyComponent extends Component {
state = { person: { id: 1, name: "Liero" } }
updateName = (e) => {
this.setState({
person: {...this.state.person, name: e.target.value}
})
}
render (){
return (
<input type="text" onChange={this.updateName} value={this.state.person.name} />
);
}
}

Resources