Is there a way to update the state with a state structure like this
this.state = {
structure: [
{
selected: false,
name: "a",
key: "a",
}, {
selected: false,
name: "b",
key: "b"
}, {
selected: false,
name: "c",
key: "c",
}, {
selected: false,
name: "d",
key: "d"
}
]
}
I want to update the state. I am doing it this way:
_onPress = (obj, index) => {
const oldStateSelected = obj.selected;
const newStateObject = Object.assign({}, obj);
newStateObject.selected = !oldStateSelected;
const oldState = _.cloneDeep([...this.state.structure]);
oldState.splice(index, 1);
const newState = oldState.push(newStateObject)
this.setState({
structure: [newState]
});
}
However, that returns me a new state of
{ structure: [4] }
I think the problem is, that I am modifiing the state in place instead of replacing it?!
When I console.log(oldState) after removing the element from the array, I see that it says oldState (3) [Object, Object, Object].
But when I open it, there are 4 array elements. The element I wanted to remove with splice is still in there.
Any ideas?
Problem is in this line:
const newState = oldState.push(newStateObject);
array.push will not return the updated array, when we use push it will update the original array, so you need to assign the variable oldState value to state variable structure.
Use this:
_onPress = (obj, index) => {
const oldStateSelected = obj.selected;
const newStateObject = Object.assign({}, obj);
newStateObject.selected = !oldStateSelected;
const oldState = _.cloneDeep([...this.state.structure]);
oldState.splice(index, 1);
oldState.push(newStateObject)
this.setState({
structure: oldState //here
});
}
Check this snippet:
let a = [10,15,20];
let b = a.push(20);
console.log('a = ', a);
console.log('b = ', b);
Array.prototype.push is not returning the array. Just push and do
this.setState({
structure: oldState
});
Related
I am deleting an one id in an array, how do I setState after filtering it here?
https://codesandbox.io/s/react-example-1m2qn
const Debtors = () => {
const debtors = [
{
id: 1,
name: "John",
relation: "friend",
statement: [
{ id: 1, date: 2010, amount: "1000", purpose: "John" },
{ id: 2, date: 2014, amount: "2000", purpose: "john" }
]
},
,
{
id: 2,
name: "Jack",
relation: "Friend",
statement: [
{ id: 1, date: 2010, amount: "1000", purpose: "jack" },
{ id: 2, date: 2014, amount: "2000", purpose: "jack" }
]
}
];
const [newDebtors, setdebtors] = React.useState(debtors);
const handleDelete = (stat, i) => {
const newList = newDebtors[0].statement.filter(x => x.id !== stat.id);
// How to set debtors here ?
// setdebtors({ ...newDebtors, statement[0]: newList });
console.log(newList)
// How to set debtors here ?
There's two problems:
1) You are iterating off the original debtors object in your render, instead of the newDebtors state you created via useState(), which is why there does not appear to be any UI change.
You need: newDebtors[0].statement.map
2) You need to pass in the item index in your handleDelete() so it knows what item in the array to update. You can have the function do something like this:
In the onClick:
<a
href="javascript:;"
onClick={() => handleDelete(stat, i, 0)}
>
In the handleDelete():
const handleDelete = (stat, i, arrayIndex) => {
const updatedDebtors = newDebtors.map((item, index) => {
if (index === arrayIndex) {
return {
...item,
statement: item.statement.filter(
statement => statement.id !== stat.id
)
};
} else {
return item;
}
});
setDebtors(updatedDebtors);
};
See sandbox for full solution: https://codesandbox.io/s/react-example-x7uoh
You should do it like that:
setdebtors((prevState) => {
let newArray = Array.from(prevState); // Copy the array
// Manipulate the array as you wish
return newArray; // return it
});
The problem is you are mutating the array of "debtors" you need to map through the array of debtors and change any properties in the object.
const handleDelete = (stat, i) => {
const newList = newDebtors.map((debtor, i) => {
if (i === 0) {
debtor.statement = debtor.statement.filter(x => x.id !== stat.id);
}
return debtor;
});
setdebtors(newList);};
An even better approach is to use "useReducer" which is used for mutating more complex pieces of state, like you have here. THe docs are very helpful useReducer
Hmm I dont know what exactly you are trying to do,
Is this what you are looking for?
const handleDelete = (stat, i) => {
const newList = newDebtors[0].statement.filter(x => x.id !== stat.id);
const newFirstItem = {...newDebtors[0],statement: newList}
const newDebtorList = newDebtors.filter(x => x.id !== newFirstItem.id);
newDebtorList.unshift(newFirstItem);
setdebtors(newDebtorList);
}
I know this seems complex but you kinda actually need to do this as you cannot mutate an array in the state...
What I did here is I first created a new statement list(newList), then created a newFirstItem to be set as the new newDebtors[0], then created a new array(newDebtorList) of all the elements of newDebtors except the first one, I modified this array by pushing the newFirstItem to the 0th position(using unshift)
Finally updated the state with this new array...
hope it helps :)
Note: this is for changing the 0th element if you have the id please change the code accordingly
I am trying to update the property of an object which is stored in an array.
my state looks something like this:
state = {
todos: [
{
id: '1',
title: 'first item,
completed: false
},
{
id: '2',
title: 'second item,
completed: false
}
],
}
What I am trying to do is access the second element in the 'todos' array and update the completed property to either false -> true or true -> false.
I have a button with the handler for update, and my class method for the update looks like this:
onUpdate = (id) => {
const { todos } = this.state;
let i = todos.findIndex(todo => todo.id === id);
let status = todos[i].completed
let updatedTodo = {
...todos[i],
completed: !status
}
this.setState({
todos: [
...todos.slice(0, i),
updatedTodo,
...todos.slice(i + 1)
]
});
}
While this does work, I want to find out if there is a more concise way of achieving the same result; I tried to use Object.assign(), but that didn't work out because my 'todos' is an array, not an object. Please enlighten me with better code!
It would be best to use update function to make sure you don't work on outdated data:
onUpdate = (id) => {
this.setState(prevState => {
const copy = [...prevState.todos];
const index = copy.findIndex(t => t.id === id);
copy[index].completed = !copy[index].completed;
return { todos: copy }
})
}
You can simply copy your todos from state, then make edits, and after that put it back to the state
onUpdate = (id) => {
var todos = [...this.state.todos]
var target = todos.find(todo => todo.id == id)
if (target) {
target.completed = !target.completed
this.setState({ todos })
}
}
I would like to map one array of object into another in a more functional style, I am using typescript.
Basically I am using delete to remove a property on a object, I would like to know if there is a better way to write it.
const data = props.data.map(d => ({
order: d.position,
logs: d.batches.map(b => {
let log= {
amount: b.scrap,
batchNumber: '', // NO GOOD
}
if (!b.batch || b.batch.length === 0) {
delete log.batchNumber // NO GOOD
}
return log
}),
}))
example input data:
const data = [
position: 1,
batches: [
{batchNumber: '', ammount: 3}
]
]
result:
const data = [{
order: 1,
logs:[ {ammount:3}]
}
]
You can do another map on the batches to return a new array of objects, and attach that to your returned object instead:
const out = data.map(({ position: order, batches }) => {
const logs = batches.map(({ batchNumber, ammount }) => {
if (batchNumber) return { batchNumber, ammount };
return { ammount };
});
return { order, logs }
});
DEMO
One approach would be to make a shallow copy of the target omitting keys you want to delete, for example:
let drop = key => obj => Object.keys(obj).reduce((r, k) =>
k === key ? r : {...r, [k]: obj[k]}, {});
let test = [
{foo:11, bar:2, baz: 3},
{foo:22, bar:2, baz: 3},
{foo:33, bar:2, baz: 3},
];
console.log(test.map(drop('bar')));
To add another option to the mix: it is possible to use Object.assign to optionally assign the property:
const data = [{
position: 1,
batches: [{batchNumber: '',ammount: 3}, {batchNumber: 'withNr',ammount: 4}]
}];
const res = data.map(d =>
({
order: d.position,
logs : d.batches.map(({ammount, batchNumber}) => Object.assign({ammount}, batchNumber ? {batchNumber} : null ))
})
);
console.log(res);
constructor (props) {
super(props)
this.state = { items: this.props.items }
// items props is: [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}]
}
onClick () {
const myArray = this.state.items
const ids = ['45', '73']
ids.forEach((id, index) => {
myArray.find(x => x.id === id).foo = index
})
}
I need to change foo value to index value. So the result should look like
myArray = [{'id':'73','foo': 1},{'id':'45','foo': 0}]
I think with this, I do get the current value, but the syntax is wrong to change its value:
myArray.find(x => x.id === '45').foo = 'new'
I do get the error Uncaught TypeError: Cannot assign to read only property 'foo' of object '#<Object>'
You can use map to change the property you want:
const myArray = [{ id: '73', foo: 'bar' }, { id: '45', foo: 'new' }, { id: '46', foo: 'do not change me' }]
const ids = ['45', '73']
const newArr = myArray.map(item => {
if (ids.indexOf(item.id) !== -1) {
return {
...item,
foo: 'newFooValue'
}
}
return item
})
console.log(newArr)
It's also important to note that change object properties directly (such as myArray.find(x => x.id === '45').foo) is not a good practice though.
In case itemsis in you state, you can simple change it by:
this.setState(prevState => ({
items: prevState.items.map(/* same code as in the snippet above */),
}))
My main object looks like this:
const obj = {
data: {
'Puppies' : [],
'Kittens': []
}
};
I want to assign a value to the data field in data.Puppies[0].
When I try to do this using Object.assign() I get an error:
Unexpected token, expected , (83:12)
81 |
82 | return Object.assign({}, obj, {
> 83 | data.Puppies[0]: list
| ^
84 | });
85 | }
86 |
I'm not sure how I can accomplish my task.
I need to use Object.assign() because I need to return a new object not the original one. I am doing this because of a Redux reducer in ReactJS.
ES6 way without mutations:
const obj = {
data: {
'Puppies' : [],
'Kittens': []
}
};
const data = Object.assign({}, obj.data, {
Puppies: [...obj.data.Puppies, 'newValue']
});
const newObject = Object.assign({}, obj, {data});
console.log(newObject);
You could assign the outer object and assign to the array the new content at index 0 without mutating the original obj.
const list = ['foo', 'bar'];
const obj = { data: { Puppies: [], Kittens: [] } };
console.log(Object.assign({}, obj, { data: { Puppies: Object.assign([], obj.data.Puppies, { 0: list }) } }));
console.log(obj);
The same with content for Puppies.
const list = ['foo', 'bar'];
const obj = { data: { Puppies: ['bar', 42], Kittens: [] } };
console.log(Object.assign({}, obj, { data: { Puppies: Object.assign([], obj.data.Puppies, { 0: list }) } }));
console.log(obj);
You could try using Ramdajs lens to achieve this
const obj = {
data: {
'Puppies' : [],
'Kittens': []
}
};
const list = [1,2,3,4];
const xlens = R.lensPath(['data', 'Puppies'])
console.log(R.set(xlens, list, obj))
Here is the code repel
Replace Puppies with a new array with list as element 0 and the rest of Puppies starting with element 1, so effectively replacing element 0.
var obj = { data: { 'Puppies': [], 'Kittens': [] } };
var list = [ 'a', 'b' ];
var n = Object.assign({}, obj, {
data: Object.assign({}, obj.data, {
Puppies: [ list ].concat(obj.data.Puppies.slice(1))
})
});
console.log(obj);
console.log(n);