I'm trying to set a state that I'm getting from an input target:
Below is the code for my constructor and the method which updates the target's state
constructor(props) {
super(props);
this.state = {
title: ''
};
this.onChange = this.onChange.bind(this);
}
onChange(e) {
this.setState({
[e.target.name] : e.target.value
});
}
Where the input is given by this code:
<input type={"text"} name={"title"} value={this.state.title} onChange={this.onChange}/>
When I debug my code I found that e.target.name contains "title" and I do not understanding why I need the squared brackets [] in [e.target.name] ... I found some explanation on the web but I have not understood what it means:
We use attr in this.setState with square brackets [ ] because [ ] lets
us query object key names programmatically (similar to how array[2] or
object[keyA] works)
Can you help me to understand why we need these brackets ?
FYI : If I remove the brackets as this :
onChange(e) {
this.setState({
e.target.name : e.target.value
});
}
this give me this error : "Unexpected token, expected" at e.target.name
This syntax is just another way to set a key of an object without knowing ahead of time what you want it to be called -- a computed property name.
For instance, these two examples accomplish the same thing:
const myNewObject = {
name: 'Joe',
age: 30
}
…
const propName = 'age'
const myNewObject = {
name: 'Joe',
[propName]: 30
}
So in your example– e.target.name comes from the name attribute of the input element ("title", in this case). By setting the state's key with [e.target.name] you're really just setting the title property of the state.
{ sth: else }
is equal to
{ ["sth"]: else }
or
var id = "sth";
{ [id]: else }
So you basically need it when you want to evaluate the identifier instead of taking it.
Related
I have an array of default values in a useState. Upon an onChange event via a select, the properties in the array are changed to undefined. I've been reading up on controlled components and I think I've missed something silly.
Here is a codesandbox.
const [config, setConfig] = useState([{
carType: "Sedan",
carColor: "Red"
}]);
// onChange
const setCarType = (e) => {
setConfig({ ...config[0], carType: e.target.value });
};
const { carType, carColor } = config[0];
I thought that I could use the spread operator here in order to copy the two properties of config[0] into two separate constants. I originally had the config in an object but was thrown an error "Objects are not valid as a react child".
const setCarType = (e) => {
setConfig([{ ...config[0], carType: e.target.value }]);
};
const setCarColor = (e) => {
setConfig([{ ...config[0], carColor: e.target.value }]);
};
When you were setting config, you were setting a new object as config but it was initialized as an array of objects. So config[0] was undefined cause config was no more an array. Try the above code, it will solve the issue.
I want to update value of one object only but updating value of one Object, Updates the value for all objects.
let default = {
name: '',
age: ''
}
this.state = {
values: Array(2).fill(default)
}
updateName (event) {
let index = event.target.id,
values = this.state.values;
values[index].name = event.target.value;
this.setState ({
values: values
});
}
There are four significant problems in that code.
You're using the same object for all entries in your array. If you want to have different objects, you have to create multiple copies of the default.
You're calling setState incorrectly. Any time you're setting state based on existing state (and you're setting values based, indirectly, on this.state.values), you must use the function callback version of setState. More: State Updates May Be Asynchronous
You can't directly modify the object held in this.state.values; instead, you must make a copy of the object and modify that. More: Do Not Modify State Directly
default is a keyword, you can't use it as an identifier. Let's use defaultValue instead.
Here's one way you can address all four (see comments):
// #4 - `default` is a keyword
let defaultValue = {
name: '',
age: ''
};
this.state = {
// #1 - copy default, don't use it directly
values: [
Object.assign({}, defaultValue),
Object.assign({}, defaultValue),
] // <=== Side note - no ; here!
};
// ....
updateName(event) {
// Grab the name for later use
const name = event.target.value;
// Grab the index -- I __don't__ recommend using indexed updates like this;
// instead, use an object property you can search for in the array in case
// the order changes (but I haven't done that in this code).
const index = event.target.id;
// #2 - state updates working from current state MUST use
// the function callback version of setState
this.setState(prevState => {
// #3 - don't modify state directly - copy the array...
const values = prevState.values.slice();
// ...and the object, doing the update; again, I wouldn't use an index from
// the `id` property here, I'd find it in the `values` array wherever it
// is _now_ instead (it may have moved).
values[index] = {...values[index], name};
return {values};
});
}
Note that this line in the above:
values[index] = {...values[index], name};
...uses property spread syntax added in ES2018 (and shorthand property syntax, just name instead of name: name).
I would use the Array.prototype.map function with combination of the object spread syntax (stage 4):
Note that i changed the name of the default object to obj.
default is a reserved key word in javascript
let obj = {
name: '',
age: ''
}
this.state = {
values: Array(2).fill(obj)
}
updateName(event){
const {id, value} = event.target;
this.setState(prev => {
const {values} = prev;
const nextState = values.map((o,idx) => {
if(idx !== id)
return o; // not our object, return as is
return{
...o,
name: value;
}
});
return{
values: nextState
}
});
}
There is an easy and safe way to achieve that through the following:
this.setState({
values: [ newObject, ...this.state.values],
});
this will create an instance of the state and change the value of an existing object with new object.
I am trying to make a search box which filters some data (object) when users put search term and display the result.
This is the code.
state = {
names: customerData.name,
searchTerm: ''
};
editSearchTerm = (e) => {
this.setState({searchTerm: e.target.value})
}
dynamicSearch = () => {
return this.state.names.filter(name =>
name.toLowerCase().includes(this.state.searchTerm.toLocaleLowerCase()))
}
This is the data.
customerData = [
{
"index" = 0,
"age" = 20,
"name" = "A"
},
{
"index" = 1,
"age" = 30,
"name" = "B"
}
]
when I execute the code it returns an error message "TypeError: Cannot read property 'filter' of undefined".
Looks like
names: customerData.name
is not the right way of getting the value of the data. What is the correct way to get the value from it?
When you define the state, give an empty array [] to names.
state = {
names: [],
searchTerm: ''
};
And change the names state when you get the customData. Don't forget to check if customData.names is a valid array. Please check if customData.names is returning array.
if (customData.names) {
this.setState({
names: customData.names
})
}
Imagine that I have this kind of JSON object on my react state:
this.state={
parent:{
childs:[
child1:{
},
child2:{
},
child3:null,
(...)
]
}
}
to delete the child1 I did the following method:
deleteChild1 = (index,test) => {
const childs= [...this.state.parent.childs];
childs[index] = {
...childs[index],
child1: null
}
this.setState(prevState => ({
...prevState,
parent: {
...prevState.parent,
childs: [
...childs,
]
}
}))
}
This works with no problem, but imagine that I have 100 childs, I have to do 100 methods like this but instead putting the child1 to null I have to put the child100, child99, you get the idea.
My question is that is another way to put the variable null.
Thanks!
Currently your data structure isn't valid so its hard to write up a solution. If I try to create that exact state object it raises an exception Uncaught SyntaxError: Unexpected token : This is because you have an array written like an object. So first thing to do is adjust your state model to be valid syntax
this.state = {
parent: {
childs: {
child1: {
},
child2: {
},
child3: null,
...
}
}
}
Now, what you are describing / what you want to do is a dynamic key reference.
You can do this like so
const childTarget = 'child1'
childs = {
...childs,
[childTarget]: null
}
so to abstract that concept a little more. make it a function parameter
deleteChild = (childTarget) => {
then when you want to remove any particular child you can let them pass their value to this function
<Child onRemove={this.deleteChild} name={name} />
// name here would be like 'child1'
// assuming you are looping over state and rendering a child component for each item
and when the child calls this function
this.props.onRemove(this.props.name)
The answer is very simple, this would be my approach.
Create a function which updates your state with the expect result (removing that property).
If you wish to assign null that you can replace .filter() with a .map() solution. Typically if you are removing a piece of data it does not make sense to null it, but to remove it.
FYI your property childs is an array you have an object within, so you need a list of objects to fix this.
E.g.
[
{
name: "child1"
},
{
name: "child2"
},
{
name: "child3"
}
]
removeChild = (child) => {
const newChildList = this.state.parent.childs.filter(({name}) => name !== child);
this.setState(previousState => ({
...previousState,
parent: {
...previousState.parent,
childs: newChildList
}
}));
}
The key part here is that the data is being updated and overriding the original array. Because you have nested data structure we don’t want to delete any pre-existing data (hence the spreading).
Call the function however you want and if your childs array has an object with the property called name that matches the child function argument, it will be not be present on the next re-render.
Hope this helps.
My state looks like this
state = {
basic: {
entry: 'index.js',
output: {
path: 'dist',
filename: 'bundle.js',
}
}
}
I have defined a callback for input onChange event :
handleUpdateString = (e) => {
const name = e.target.name
const value = e.target.value
this.setState({ [name]: value })
console.log(this.state)
}
say my input name is 'basic.entry'
my state is updated, but instead of having this.state.basic be { entry: 'some value'}
I end up with a new key in my state : { basic.entry: 'some value' }
I have tried using dot-object to convert the dot-seperated string to a nested object, and passing this to setState but the state appears to be unchanged.
What are simple solutions to this problem ?
Use a switch statement with explicit setState.
This is not a React question but a pure JavaScript question on how to set a nested property on any object using a dot-notated string for the property. There is an answer here: Javascript object key value coding. Dynamically setting a nested value
Using that example in React:
handleUpdateString = (e) => {
const name = e.target.name
const value = e.target.value
const state = Object.assign({}, this.state);
setData(name, value, state);
this.setState(state);
console.log(this.state)
}