i want to create state like this:
componentWillReceiveProps(nextProps) {
nextProps.columns.forEach((c) => {
const name = nextProps.columns[nextProps.columns.indexOf(c)];
this.setState({ `${name}`: (this.props.activeHeaders.indexOf(c) > -1) });
console.log(`${name}`);
});
}
I am mapping on my array columns, so each item on the array, i want to set state on them as key, is there a possibe way?
Is there a possible way?
Yes, but the way you are trying is not correct, instead of calling setState inside loop, first prepare an object with all the key-value, then pass that object to setState.
Like this:
componentWillReceiveProps(nextProps) {
let obj = {};
nextProps.columns.forEach((c, i) => {
const name = nextProps.columns[nextProps.columns.indexOf(c)];
obj[name] = this.props.activeHeaders.indexOf(c) > -1;
});
this.setState(obj);
}
Didn't get the meaning of this line:
const name = nextProps.columns[nextProps.columns.indexOf(c)];
componentWillReceiveProps(nextProps) {
nextProps.columns.forEach((c) => {
const name = nextProps.columns[nextProps.columns.indexOf(c)];
this.setState({ [name]: (this.props.activeHeaders.indexOf(c) > -1) });
console.log(`${name}`);
});
}
This should do the job
Related
people, I have a question. I have two javascript functions that do the same thing. Note that it only changes the "let selected" variable. I don't think it's the best way to use functions in js, how can I reuse them?
First function:
onChange(id) {
let selected = this.state.selectedDevice
let find = selected.indexOf(id)
if(find > -1) {
selected.splice(find, 1)
} else {
selected.push(id)
}
this.setState({ selected })
}
Second function:
onChangeSec(id) {
let selected = this.state.selectedSection
let find = selected.indexOf(id)
if(find > -1) {
selected.splice(find, 1)
} else {
selected.push(id)
}
this.setState({ selected })
}
thanks !!!
You could just pass key as another function parameter
onChange(id, key) {
let selected = this.state[key]
let find = selected.indexOf(id)
if(find > -1) {
selected.splice(find, 1)
} else {
selected.push(id)
}
this.setState({ selected })
}
and in your case key would be 'selectedDevice' or 'selectedSection' passed as a string.
It seems like this.state.selectedDevice and this.state.selectedSection are both arrays, and since you're using index-based retrieval from the array, there's no way from within the function to determine which should be used. Instead you should pass that information into the function. For example:
onChange(id, data) {
const position = data.indexOf(id)
this.setState({
selected: position > -1
? [...data.slice(0, position), ...data.slice(position + 1)]
: [...data, 1]
})
}
Note: this also works around your mutation of an object in state, which will cause bugs.
You might also want to consider using a Set instead:
constructor() {
this.state = {
selected: new Set()
}
}
onChange(value) {
const newSelected = new Set(this.state.selected)
if (newSelected.has(value)) {
newSelected.delete(value)
} else {
newSelected.add(value)
}
this.setState({ selected: newSelected })
}
This is safer than an array where you can have multiple entries of the same value.
This function works properly and the component rerenders
handleRemove = (e) => {
//console.log(e);
const arrayCopy = this.state.scanlist.filter((row) => row.ref + row.lot !== e.ref + e.lot);
this.setState({ scanlist: arrayCopy });};
This function changes the state but the component does not rerender
handleAdd = (e) => {
//console.log(e);
const index = this.state.scanlist.findIndex((row) => row.ref === e.ref && row.lot === e.lot);
let scancopy = this.state.scanlist;
scancopy[index].qty = scancopy[index].qty + 1;
console.log(scancopy);
this.setState({ scanlist: scancopy });};
Does anyone see the issue? Mutation?
Using Array.prototype.filter to remove an element from an array is pretty standard, but in the second handler yes, you've a state object mutation.
handleAdd = (e) => {
const index = this.state.scanlist.findIndex((row) => row.ref === e.ref && row.lot === e.lot);
let scancopy = this.state.scanlist;
scancopy[index].qty = scancopy[index].qty + 1; // <-- state object mutation
this.setState({ scanlist: scancopy });
};
You should shallow copy the scanlist array and the element that is being updated. Array.prototype.map is a common method to shallow copy the state array, and spreading the object properties of the element that needs to be updated shallow copies it. Anytime you are updating any nested state objects you should shallow copy the parent object.
handleAdd = (e) => {
this.setState(prevState => ({
scanlist: prevState.scanlist.map(
(row) => row.ref === e.ref && row.lot === e.lot ? {
...row,
qty: row.qty + 1,
} : row)
}));
};
In the first approach filter method returns new list, whereas in the second approach it is just referencing to state array.
Try shallow copy in second approach,
let scancopy = [...this.state.scanlist]
Or
Simple deep copy approach,
let scancopy = JSON.parse(JSON.stringify(this.state.scanlist));
I have a function
checkName(output) {
output.filter((NewData) => {
return this.props.elements.filter((OldData) => {
if (NewData.key == OldData.key) {
NewData.name = OldData.name,
//there i need to add another element
// Need to add newData.number = OldData.number
}
return NewData
})
})
return output
}
and I call this function like:
const named = this.checkName(product.rows)
Now I need to add to my product's array that I passed to checkName the value "OldData.Number" to "newData.Number" that is not defined in product (so I need to create this field)
For example:
Product before the checkName function
product.rows = [NewData.name]
Product after the checkName function
product.rows = [NewData.name="value of OldData.name", NewData.number="value of OldData.number"]
How can I obtain this result?
There are 2 confusing things in your code:
You are using filter to execute an action in each member of the output array. However, filter should be used to... well, filter that array, meaning that is should not modify it, just return a sub-set of it. Instead, you might want to use forEach. However, taking into accound the next bullet, probably you want to use map.
You are modifying the array passed to the checkName function. This is confusing and can lead to hard-to-find bugs. Instead, make your function "pure", meaning that it should not mutate its inputs, instead just return the data you need from it.
I would suggest some implementation like this one:
checkName(output){
return output.map((NewData) => {
// find the old data item corresponding to the current NewData
const OldData = this.props.elements.find(x => x.key === NewData.key);
if (OldData) {
// If found, return a clone of the new data with the old data name
// This uses the spread syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
return {
...NewData, // Clone the NewData object
name: OldData.name, // set the value found in OldData.name in the "name" field of the cloned object
number: OldData.number, // You can do the same for each field for which you want to replace the value cloned from NewValue
};
} else {
// Otherwise, just return a clone of the NewData
return { ...NewData };
}
}
}
The usage would be like this:
const named = this.checkName(product.rows)
Be aware that the product.rows array won't be modified!
You can get keys and values of the old object.
const keys = Object.keys(oldObject);
const values = Object.values(oldObject);
// or
const [keys, values] = Object.entries(oldObject);
After, you will create a loop with all keys of oldObject, and insert in newObject like a array.
keys.forEach( (key, index) => newObject[key] = values[index]);
// or
for (const [key, value] of Object.entries(object1)) {
newObject[key] = value
}
Use map like this.
checkName(output){
return output.map(( NewData) =>{
this.props.elements.forEach((OldData) => {
if (NewData.key == OldData.key) {
NewData.name = OldData.name;
NewData.number = OldData.number;
}
})
return NewData;
})
// return output;
}
Hey I'm trying to remove a key:value pair from state inside a Javascript Object.
It works when I hardcode the key name in the code, but when I try to use a variable from a function call, it does nothing.
Can somebody help me out?
Here's an object example:
toppingsSelected: {
"Onion":"true",
"Mushrooms":"true",
}
This works, hardcoded:
deleteTopping = toppingName => {
const { Onion, ...withoutOnion } = toppingsSelected;
console.log(withoutOnion); // Returns object without onion
};
This doesn't work:
deleteTopping = toppingName => {
const toppingName = "Onion"; // Variable gets passed in
const { toppingName, ...withoutOnion } = toppingsSelected;
console.log(withoutOnion); // Returns original object, no change made
};
So I'm basically trying to remove a key from React state but I'm pretty new to Javascript.
How can I make Javascript aware that toppingName is a key?
Another option is to add square brackets arround toppingName, and assign it to a variable. As #Bergi pointed out in the comments, this option does not mutate toppingsSelected
const toppingsSelected = {
"Onion":"true",
"Mushrooms":"true",
};
const toppingName = "Onion";
const {
[toppingName]: topping,
...withoutOnion
} = toppingsSelected;
console.log(JSON.stringify(withoutOnion));
To set the React state, you'd then do this
this.setState({ toppingsSelected: withoutOnion })
You can use delete e.g.
delete toppingsSelected[toppingName];
One way of doing this is using Array.prototype.filter()
const _obj = {
'Onion': true,
'notOnion': false
};
const newObj = Object.keys(_obj)
.filter(key => key !== 'Onion')
.reduce((acc, cur) => ({ ...acc, cur }), {})
console.log(newObj); // { notOnion: false }
This will return a new object without the 'Onion' property
Have filter, it's filtering ok, but when clear input. I see filtered result:
filterList(event) {
var updatedList = this.state.items;
if (event.target.value !== '') {
updatedList = updatedList.filter(function(item){
return item.name.toLowerCase().indexOf(event.target.value.toLowerCase()) !== -1;
});
}
this.setState({items: updatedList}); // now this.state.items has a value
}
My condition not working.
https://plnkr.co/edit/dPZM9BZVa4uzEMwdNpZ8?p=catalogue full component in script.js
There's a better approach for this. You usually don't want to use props to set your state directly. What you want is to only use the state for filtered item sets, since your props will always contain the full set of items. So first, remove your componentWillReceiveProps function. Then change the following things:
// in your constructor
this.state = {
filteredItems: false
}
filterList(e) {
var value = e.target.value;
this.setState({
filteredItems: !value
? false
: this.props.items.filter(function(item) {
return item.name.toLowerCase().indexOf(value.toLowerCase()) > -1
})
})
}
// inside render
var elems = this.state.filteredItems || this.props.items