Trying to update one element of an array in this.state I'm getting an (expected ,) error but cant see where I've gone wrong. Do I need to create a temporary array update that, then assign the whole array back to the state
This is essentially what I have
this.state = { array: ['a', 'b', 'c'] };
onBack() {
this.setState({
array[2]: 'something'
});
}
You can't update the state like this.
Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
Read React docs.
You can do something like this :
let newArray = [...this.state.array];
newArray[2] = 'somethingElse';
this.setState({array: newArray});
The above example is using Spread Syntax.
There are multiple ways to modify state, but all the ways should ensure that data is treated as immutable. You can read more about handling state in React here
Manoj's answer will work, there is another way using the updater function (funtinal setState) for setState (which you can read about here)
onBack() {
this.setState(prevState => {
let newArray = prevState.array
newArray[2] = "something"
return { array: newArray }
});
}
Use Object.assign({});
let array= Object.assign({}, this.state.array); //creating copy of object
array[2]="something"
this.setState({array});
If you are bother to set the new updated array on your currentArray using spread function, you may use Array.from(duplicateArray)
let duplicateArray = [...currentArray] //duplicate the array into new array
duplicateArray[index].value = 'x'; // update the field you want to update
setCurrentArray(Array.from(duplicateArray )) //set the new updated array into currentArray
I bother with this issue and I just search about and I saw this idea, and it works!
https://github.com/facebook/react-native/issues/21734#issuecomment-433885436
using hooks
const [totalSet, setTotalSet] = useState(null);
console.log(totalSet); // {sets: Array(3), subscribed: false}
setTotalSet(datas => ({
...datas,
sets: updatedSet,
}));
Create a temporary array update that, then assign the whole array back to the state
var arr = [];
arr = this.state.array;
arr[2] = "something";
this.setState({
array : arr
});
Related
After executing the newData[0].id = newValue I am actually updating the react initialData state. How is that possible?
Is my understanding that filter should return a new array different than the original one, also I am not ussing the setState feature so I don't understand why the state is changing.
Because arrays are mutable. it will keep the reference to the original array even after filtering.
use the spread operator to avoid mutating the original array
const data = [...newData]
data[0].id = newValue
As per the new beta docs on updating items in array
setInitialData(prev => {
// create a new array
const withReplaced = prev.map(elem => {
if (elem.id === id) {
const newVal = //set new value
// create a new updated element
return {
...elem,
id: newVal
}
} else {
// The rest haven't changed
return elem;
}
});
return withReplaced;
})
Hope it helps
you can't update the initialData,but the you can update the son of the array.And if you don't use "setData".The views won't change.
setListOfPosts(curPosts => {
let newPosts = [...curPosts];
newPosts[newPosts.findIndex(p => p.id === postId)].alert = response.data;
}
});
//is curPosts an instance of array or complete array?? my listofPosts is an array of objects
Your setState call needs to return newPosts, and you're creating an array using the spread operator which is why it's coming back as an array of objects.
I'm not sure what your desired output is, but by adding a return function it will set the state:
setListOfPosts(curPosts => {
let newPosts = [...curPosts];
newPosts[newPosts.findIndex(p => p.id === postId)].alert = response.data;
return newPosts
}
});
This is untested but if your logic is correct should return an array of objects with the objects alert value updated.
Another option would be to do your logic before your setState call, by creating a a newState array and then simply updating the state with that new array without the use of the callback.
The callback function is useful if you want to add a new object to state array or do something that preserves the initial state, in your example you could do it without the callback like this:
// Create a copy of the state array that you can manipulate
const newPosts = [...newPosts]
if (data.response) {
// Add your logic to the state copy
newPosts[newPosts.findIndex(p => p.id === postId)].alert = response.data;
// Replace state with state copy
setListOfPosts(newPosts)
}
Again untested but hopefully this should help you understand the use of the callback function and the right way to use it.
Cannot figure out why this state will not update, there are other values other than personalValues that is why it is nested.
The state
this.state = {
personalValues: [
{targetLevel: 0},
{firstCommission: 0.36},
{cancels: 0.10},
{averagePolicy: 1150},
{productionWeeks: 48},
{presPerWeek: 15},
{closingRate: 0.40},
{appsPerWeek: 6}
]
The handler I tried
handleFormChange(event){
this.setState({[event.target.name]: event.target.value})
}
The Component
const personalFormValues =
{
class: ["firstCommission", "cancels", "averagePolicy",
"productionWeeks", "presPerWeek", "closingRate",
"appsPerWeek"],
};
var className = personalFormValues.class[i];
<TextField
className={className}
type="number"
placeholder={personalFormValues.placeholder[i]}
onChange={this.handleFormChange}
onBlur={this.changeValues}
name={className}
fullWidth={true}
pattern="[0-9]*"
required />
In terms of the state there is only a single value this.state.personalValues. To React it doesn't matter if that value is an array of objects. Instead simply create a variable that stores the current value let obj = this.state.personalValues. Then iterate through obj until you find a match on the key with event.target.name and set the value of that obj to event.target.value. Or more simply doesn't use an array and use an object directly:
this.state = {
personalValues: {
targetLevel: 0,
firstCommission: 0.36,
cancels: 0.10,
averagePolicy: 1150,
productionWeeks: 48,
presPerWeek: 15,
closingRate: 0.40,
appsPerWeek: 6
}
Then you could just do:
let obj = this.state.personalValues;
obj[event.target.name] = event.target.value;
this.setState({personalValues: obj});
React checks elements of this.state for shallow pointer equivalency before triggering a re-render.
Therefore, if you in-place-modify a recursive data-structure, then React will fail to notice a change, because shallow comparison between previous and next state object shows no difference.
Thus you cannot write to a recursive structure, nor do:
this.setState(prevState => ({personalValues: Object.assign(prevState.personalValues, nextValues)}));
because Object.assign performs in-place modification.
You must do either of:
this.setState(prevState => ({personalValues: Object.assign({}, prevState.personalValues, nextValues)}));
this.setState(prevState => ({personalValues: {...prevState.personalValues, ...nextValues}}));
This will create a new object and copy the data-structure in it. Be aware that only the first level object is created anew.
Recursive copying gets ugly; see https://stackoverflow.com/a/43041334/1235724
Nonetheless, if you have a very large data structure stored in your state,
you may want to use in-place modification together with this.forceUpdate(),
eschewing copying altogether.
Do you need personalValues to be an array? If you specify it like this, that should work:
state = {
personalData: {
targetLevel: 0,
firstCommission: 0.36
....
}
}
handleFormChange({target}){
this.setState(prevState => ({
personalValues: {
...prevState.personalValues,
[target.name]: target.value
}
})
}
Better way to copy old state to a new variable then change the values if you want and then update the state value with that newly created type variable.
let personalValuesCopy = this.state.personalValues;
personalValues.dynamicKey = updated values // Update values may be using onChange events or some custom data
this.setState({ personalValues : personalValuesCopy }) // Update state with new object
In the function below, I am trying to update the state of a react component; the animalMix item is an array. I take a copy, update it and then try to overwrite the original. I have checked that the new array (newAnimalsHeld) is updated correctly, but this is not reflected when i set animalMix in state equal to it.
The whole thing can be seen in context here:
https://codepen.io/timsig/pen/XVdbdo?editors=0010
Many thanks for any help.
removePair(){
console.log('Match!');
console.log(this.flipped);
let animalsHeld = [...this.state.animalMix];
let theMatch = this.flipped[0].props.animal;
let newAnimalsHeld = animalsHeld.map(function(animal){
if (animal.animal === theMatch) {
console.log('MATCH! Animal: ' + animal.animal + 'Match:' + theMatch);
return {};
}else{
return animal;
}
});
console.log('New Animals held: ', newAnimalsHeld);
this.setState({
animalMix: newAnimalsHeld,
doTurn: true
});
this.flipped = [];
console.log(this.state.doTurn, this.state.animalMix);
}
setState is an asynchronous function. However, you can print to console after state has updated in the following manner:
this.setState({
animalMix: newAnimalsHeld,
doTurn: true
},() => {console.log(this.state.doTurn, this.state.animalMix);});
In my top level component I have a function to update state. I pass this down to different child elements so that they can update my main state.
In my top level component:
updateValue(item, value) {
this.setState({[item]: parseInt(value)});
}
This has worked so far however now I need to update the nth item in an array.
My top level state is like this:
this.state = {
chosenExercises: [
'Bench press',
'Squat',
'Pull up'
]
};
And in my child component Im trying to do something like:
this.props.updateValue('chosenExercises'[1], 'New exercise');
So that my state would then be:
this.state = {
chosenExercises: [
'Bench press',
'New exercise',
'Pull up'
]
};
Am I going about this the correct way? Or should my state be key value pairs?
this.state = {
chosenExercises: {
0: 'Bench press',
1: 'New exercise',
2: 'Pull up'
}
};
This would potentially solve some of my problems of making the exercises easier to target but Im not sure which is best practice.
Since the chosenExercises can be multiple it makes sense to make it as an array, however you need to update your state differently. Instead of passing the index of the array element to update, you should actually make a copy of the array, update it in the child element and then send the updated array to the parent.
You could do something like:
In Child:
updateValue = (item, index, value) => {
let newValue = [...this.props[item].slice(0, index), value, ...this.props[item].slice(index + 1)];
this.props.updateValue(item, newValue);
}
The thing with this is that your state has to remain immutable so you have to provide a new Array to update in your state. So you'll end up with something like:
this.updateValue('chosenExercises', 1, 'New exercise');
updateValue(item, index, value) {
const newArray = this.state[item].slice();
newArray[index] = value;
this.setState({ [item]: newArray });
}
The array.slice() function creates a new Array, in which you update the value by its index. Afterwards you update your component state with the new array.
If you happen to do this more often, React created an immutability helper for these things. You can read more about it here. This would let you do something like:
import update from 'react-addons-update';
this.setState({
[item]: update(this.state[item], {[index]: {$set: value } })
});
It can be done with this in the top level component:
updateValue(item, value, options) {
if (options.isChosenExercises === true) {
this.setState((prevState) => {
let newchosenExercises = prevState.chosenExercises.slice();
newchosenExercises[item] = value;
return {chosenExercises: newchosenExercises};
});
} else {
this.setState({[item]: parseInt(value)});
}
}
For normal uses pass an empty object as the last parameter:
this.props.updateValue('setLength', e.target.value, {})}
But when you want to update the exercise array pass an object with isExercises set to true.
chooseThisExercise() {
this.props.updateValue(numberInTheArrayToChange, newExercise, {isChosenExercises: true});
}