How to bring the pushed array data outside the function,Every thing is
working fine in click function.But when Im trying to print this outside of
the function console.log(this.state.selectedSchedule[0].storeName) , It is
throwing the error:
Cannot read property 'storeName' of undefined.
Code:
this.state = {
selectedSchedule:[]
};
click(e){
console.log('e',e);
document.getElementById("detailView").style.display="block";
this.state.selectedSchedule=[];
for(let i=0;i<this.scheduless.length;i++){
if(this.scheduless[i].scheduleId === e){
this.state.selectedSchedule.push(this.scheduless[i]);
break;
}
}
console.log('cheudkl',this.state.selectedSchedule);
this.setState=({
selectedSchedule:this.state.selectedSchedule
});
console.log(this.state.selectedSchedule[0]);
console.log(this.state.selectedSchedule[0].storeName) //printing
};
//html render
render(){
console.log(this.state.selectedSchedule[0].storeName) // here it's throwing me the error(not printing)
}
Reason is, initial value of this.state.selectedSchedule is [] and during initial rendering you are trying to get the storeName of 0th items, and clear at that time 0th time doesn't exist so
this.state.selectedSchedule[0] ===> undefined
And you are trying to get the value from undefined that's why it is throwing the error. Simply put the check on length before printing the values.
Like this:
render(){
// now it will print the values once some item will be added
if(this.state.selectedSchedule.length)
console.log(this.state.selectedSchedule[0].storeName);
return (....)
}
Another issue is in the way you are updating the state value.
Changes:
1- Never mutate the state value directly, Treat this.state as if it were immutable.
2- setState is a function so we need to call it this.setState({}).
write it like this:
click(e){
document.getElementById("detailView").style.display="block";
let arr = [];
arr = this.scheduless.filter(el => el.scheduleId === e);
this.setState({
selectedSchedule: arr
});
};
You should not mutate the state, push is mutating the array, and you are re assigning the state object in the click function this.state = ....
Instead, use the builtin setState method of react to update it:
this.setState({key: value});
As for arrays you can add items with the ES6 spread operator without mutating it:
const nextState = [...this.state.selectedSchedule, newValue];
this.setState({selectedSchedule: nextState });
EDIT
As Mayank Shukla mentioned, in addition to this solution you should check to see if the length of your array is bigger then 0:
this.state.selectedSchedule.length > 0 && console.log(this.state.selectedSchedule[0].storeName);
In the first render call this array is empty hence its length is 0.
This is not correct way of updating the state
this.setState=({
selectedSchedule:this.state.selectedSchedule
});
You need to edit this as :
if(this.scheduless[i].scheduleId === e){
this.setState({selectedSchedule:[...this.state.selectedSchedule, this.scheduless[i]]});
break;
}
You need to set the state only then the render function will be called again and your changes to the state variable will be visible.
You should never mutate the state (changing the contents of this.state) directly.
That is, instead of assigning an empty array to part of the state and then push every item, create a new array:
const selectedSchedule = this.scheduless.filter(schedule => schedule.scheduleId === e)
this.setState({selectedSchedule});
Related
Im programming a component where I am mapping over a component using an array I have stored in state.
const [animalList, setList] = useState(['cat', 'dog'])
{ animalList.map((tag) => {
return (
<AnimalButton animalz={tag}/>
)
})
}
I want to add to the state array to force to make the app rerender. I attempted to do so with a .push function, store the increased array in a temporary variable and assign that new array to the state, as push() and unsplice don't return the actual array.
onSubmit={(values, actions) => {
actions.resetForm();
animalList.push(values.pet)
let newList = animalList
setList(animalList = newList)
}}
However I get this error [TypeError: "animalList" is read-only]?
Is there a way to change add to animalList without my resulting list in the state becoming read only?
Yes you cannot push this into const.
However, you can use this approach.
setList([...animalList,values.pet])
just after updating state using setState, if i log state , i am getting empty array even though state is not empty.
I have hook like shown in code below.
const [pets, setPets] = useState([]);
async function requestPets() {
const { animals: animalsProperty } = await pet.animals({
location,
breed,
type: animal
});
console.log(animalsProperty);
setPets(animalsProperty || []);
console.log(pets);
}
Inside requestPets function after setting setPets with animalsProperty, even if animalsProperty is not null, when i log pets i am seeing empty array in console.
may i know why pets is showing empty array instead of value like animalsProperty when i logged pets?
setPets does async update, so if you console.log the state value right after setting it, the previous value will still be shown because it will be updated on subsequent component's render.
Could you use callback see if it works as your function is async
setwww(b=>
{
return b||[];
});
I think pets will be updated in the render function as expected.
After you fire setPets, it won't update the value of pets immediately. In other words, you will get the same value of pets within that render. The following shown the sequence:
===Initial Render: ===
1. Pets is []
2. setPets(somePets) => trigger re-render
3. Pets is []
===Re-render: ===
4. Pets is somePets
can anyone lemme know the working of each line as i dont have any idea
can we have let in set sate? please explain these 2 lines
const userDetails = previousState.userDetails
return { userDetails: {...userDetails, [key]: value} }
the return in set state i have not understand
this.setState((previousState) => {
const userDetails = previousState.userDetails
return { userDetails: {...userDetails, [key]: value} }
})
setState can accept a function from the previous state to a new state:
this.setState(previousState => newState);
previousState => newState is an arrow function, that will accept the previous state (which will be provided by setState when it is invoked), and has to provide the new state.
In the code you posted, the new state is calculated by taking an object from the previous state called userDetails, and then appending a new key-value pair to it.
This is done using two mechanisms. The first, is the spread operator: ...userDetails which adds to the new userDetails value all of the key-value pairs that were in the original object. Next, the new value is added:
[key]: value
This means that a new pair is to be created, where the key is the value of the key variable (I suspect this is available somewhere in the function before the setState call), and the value is equal to a variable named value, which again is previously available.
This is the notation to use when you want to use a dynamic value as a key in a map.
Edit - Answering question from comment
Any function body that is more than one line needs to be put in a block { ... }. When you want to return a value from that function, a return keyword is used.
The return you see in your setState is the return from the function that is passed to setState.
I'm trying to replace whole state or at least delete all properties from it.
Before react 16 I just called these two lines
this.state = {}
this.forceUpdate()
With update to react 16, this didn't work anymore.
Currently I have this workaround in shouldComponentUpdate Method:
for (let prop in nextState) {
if (nextState.hasOwnProperty(prop)) {
delete nextState[prop];
}
}
But this 'feels' not right. So does anybody know the right way to reset whole state with new object? As far as I tested this.setState just changes the differences and leave other properties untouched.
You can't remove properties from the state, because it internally uses a merge
nextState = Object.assign({}, nextState, partialState);
So no way of removing already present keys. Only thing you can do is set the current keys to undefined.
If you know the properties, you can set them manually.
If not, you can try this to set them all to undefined:
this.setState(
Object.keys(this.state).reduce((a, c) => {
a[c] = undefined;
return a;
}, {})
)
I know how To avoid mutating objects and arrays in react state, but I am not sure about variables that are not objects and arrays like this example
this.state = {
cubeNumber: 0,
}
onNumberChange = (event) => {
this.setState({ [event.target.name]: event.target.value })
}
cubeArrayRender = () => {
let { cubeNumber } = this.state;
am I mutating state by using parseInt like this?
let cubes = parseInt(cubeNumber, 10);
or if I write like this?
let cubes = cubeNumber;
cubes = 2;
If I am mutating state, how can I avoid it?
According to React documentation you shouldn't assign state like this
// Wrong
this.state.comment = 'Hello';
But only this:
// Correct
this.setState({comment: 'Hello'});
So yeah you are not mutating state in your code :)
You can mutate state only acting directly on this.state or through setState.
You are not mutating state (or anything) by using the method you have shown in your example. parseInt returns an Integer and does not modify the String/Number that you give it, instead creating a new instance.
As per #Steve Vaughan answer. In this case you are not mutating state because the state
you used for this test is value type. But object are reference type. While you assigning
the cubeNumber to new variable cube that makes the deep copy. But that's not same for objects, you are just passing reference.
For example:
//intial state
this.state = {
user: {name:"sarath"},
age:25
}
let age=this.state.age; //deep copy
age=35; // this not going affect the original state
let user=this.state.user //Shallow copy
user.name="kumar" //It mutates the original object also
output:
original age: 25
new age: 35
name: Kumar
new name: Kumar
you can avoid the mutation by using two methods
Object Assign
This method is used to copy the values of all enumerable own properties from one or more source objects to a target object.
let user= Object.Assign({},this.state.user); //deep copy of values
user.name="kumar"; // Here the original state is untouched.
Deep cloning of entire object
Object.Assign copies property values only. It won't copy the reference inside of the Object.
For example:
//intial state
this.state = {
user: {name:"sarath",interest:{music:"western",sports:"foot ball"},
age:25
}
let user=Object.Assign({},this.state.user);
user.name="Jasrin";
user.interest.sports="cricket";
console.log(this.state.user.name); //sarath
console.log(user.name); //kumar
console.log(this.state.user.interest.sports); //cricket
console.log(this.state.user.interest.sports); //cricket
Alternate way to do deep cloning is
let user=JSON.parse(JSON.stringify(this.state.user))