I'm trying to update my state (an array of objects), but get the above error whenever I try to use .map or edit a clone of the state object.
React.useEffect(() => {
setUserMeasurements((oldUserMeasurements) => {
return oldUserMeasurements.map(nameAndMeasure => {
if (nameAndMeasure.name === name) { nameAndMeasure.measure = 60 }
return nameAndMeasure;
})
})
})
It doesn't seem to like it when I try the "nameAndMeasure.measure = 60" section of code, but I can't understand why. Can anyone explain?
I found that I could use this function to do what I was trying to get .map to do:
function replaceItemAtIndex(arr, index, newValue) { return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)]; }
The .slice methods seem to create a new array, which satisfies javascript in a way that .map doesn't.
Related
Below is the delete function which is working, it takes an argument and deletes the element with the index in the argument.
Setitem is a useState hook and is defined like this
when we click the on add list is added to item array
const listofitem = () => {
setitems((e) => {
return [...e, list];
});
};
This function is working.
const deleteItems = (e) => {
setitems((e1)=> {
return e1.filter( (er, index)=> {
return index!=e;
});
});
}
Why is this not working the splice method is working fine.
const deleteItems = (e) => {
items.splice(e-1, 1);
setitems((e1)=>{
return e1;
})
};
here is the code sandbox for the same, the function is working in this. But I want to know why the other is not working.
https://codesandbox.io/s/old-wood-cbnq86?file=/src/App.js:0-1169
This is how I got it to work, in your example you try to mutate the state directly and then not really set the state. If you want to use the state on setState you need to mutate what you call e1.
However you have some issues with your codesandbox that will trigger the function more than once sometimes
setitems((e1) => {
e1.splice(e, 1);
return [...e1];
});
Edit author updated question, this is not longer relevant.
In the first deleteItems function you never use e which I assume is the index you want to delete.
const deleteItems = (deleteIndex) => {
setitems((list)=> list.filter((_, index)=> index !== deleteIndex));
};
I am trying to mutate state by running filter and map methods, when I am returning my array the state is becoming undefined.
const getAllApprovedProviderReducer = (state=defaultState, action) => {
if(action.type === GET_ALL_APPROVED_PROVIDER) {
return {
...state
}
}
if(action.type === FILTER_BY_FINANCIAL_SERVICES) {
console.log(action.payload)
return {
...state,
success: state.success.map((item, index)=> {
item.servicesOffered.filter(i=>{
if(i===action.payload) {
return item
}
else {
// console.log(state.success);
return item
}
})
})
}
}
return state;
}
At the presentation component the state is changed to undefied.
What I am doing wrong to make state undefined.
From my comment:
The function you pass to .map does not return anything, hence every value is mapped to undefined. return doesn't cross function boundaries and the function you pass to .filter should return a boolean value.
If you only want to keep the objects which offer a specific service, then you can do that the following way:
state.success.filter(item => item.servicesOffered.includes(action.payload))
.filter will remove every element for which the callback returns false.
Using .map here doesn't make sense since it will always be a 1:1 mapping between input and output. If you want to reduce the number of elements you have to use .filter.
And while it's possible to use .filter to test for the presence of a value in an array, there are more specific methods for that, such as .includes and .some.
I am trying to assign a variable with the properties of an object from an array in my redux state. I am trying to loop through the array of objects and assign the variable when the ID of the item matches up to the ID that I am searching for.
I have been trying anything I can from nested if statements, multiple returns, I cannot seem to figure this out.
Currently, this is what I have.
const currItemProps = () => {
this.props.todos.find((todo) => {
(todo.id === this.props.itemID) ?
{ todo } : null
}
);
};
todos is my array I am searching for and the itemID is the ID I am lookking for (both are pieces of redux state).
I am trying to open a modal on the press of a todo that has the properties of the todo. Hence I am trying to assign a variable in my modal file with all of the properties of its current todo (the object from the array).
The find function expects that you'll return True when you find your item.
also, you need to specify a 'return' statement.
const currItemProps = () => {
return this.props.todos.find((todo) => todo.id === this.props.itemID);
};
In case you directly want to return model
const currItemProps = () => {
this.props.todos.find((todo) => {
(todo.id === this.props.itemID) ?
<ComponentName todoProps={todo}/> : null
}
);
};
then you can use the same in render method like {currentProps}
I am having a rather hard time tracking down the issue related to this error, obviously the implication is I'm trying to update an immutable object. Oddly, I have used this implementation before without a hitch which is why I am finding this behaviour so perplexing.
Below is the method I am using, it simply alters the object of an array, changing the property and returns the updated state. As referenced here, using prevState appears to be the optimal way for getting around immutability.
onCheck = (id) => {
this.setState((prevState) => {
prevState.options[id].checked = !prevState.options[id].checked;
return {
options: prevState.options,
dataSource: prevState.cloneWithRows(prevState.options)
};
});
}
I have also tried a number of variations of copying the prevState, however it is still giving me the same immutability error. It appears as if it still references rather than duplicates the prevState.
onCheck = (id) => {
this.setState((prevState) => {
let options = [...prevState.options];
options[id].checked = !options[id].checked;
return {
options: options,
dataSource: this.state.cloneWithRows(options)
};
});
}
I eventually found a solution, it appears I needed to copy not just the array but each element of the array. As the individual elements / objects of the array are still immutable / frozen.
onCheck = (id) => {
this.setState((prevState) => {
const newOptions = prevState.options.map((option, index) => {
let copyOption = {...option};
if (id == index) {
copyOption.checked = !copyOption.checked;
}
return copyOption;
});
return {
options: newOptions,
dataSource: this.state.dataSource.cloneWithRows(newOptions)
};
});
}
In your question, you acknowledge that you are mutating the state, which you cannot do. A fix for this is to clone prevState.options before fiddling with it.
eg.
var options = Object.assign({}, prevState.options)
options[id].checked
return {
options,
...
As your reference pointed out, you should not directly mutate your data like you do
prevState.options[id].checked = !prevState.options[id].checked;
In your link, they return a new array by concat or by "spread syntax" (...). So i suggest you to copy your prevState first and mutate it
let state = [...prevState.options];
state[id].checked = !state[id].checked;
...
I'm trying to create object from form elements. For some reason, it is throwing the error.
let allInputs = [...formData];
allInputs.pop(); //Remove submit button
return allInputs.reduce((userObj, data) => userObj[`${data.name}`] = data.value, {});
Error
userModel.js:17 Uncaught TypeError: Cannot create property 'last_name' on string ''
You need to return accumulator or in your case userObj in each iteration of reduce so your code should look like this.
allInputs.reduce((userObj, data) => (userObj[`${data.name}`] = data.value, userObj), {});
The problem is what you're returning the second time your reducer is called, not what you start with.
You are returning an assignment but should return an object.
(userObj, data) => userObj[`${data.name}`] = data.value // <-- this returns the result of the assignment
Something like this should work:
allInputs.reduce(
(userObj, data) => Object.assign(userObj, {
[data.name]: data.value
}),
{}
);
Note: as mentioned by Vic in the comment, no need for string interpolation, i.e. ${data.name} -> just data.name is enough.