In React setState(), how to refill an array? - reactjs

I'm doing a poker game practice, I want to re-shuffle the pokers each time when it is almost used up, but I cannot refill the pokers array with another array in setState(), the pokers array is empty at the end and throw the error.
deal=()=>{
// At the beginning, there are 104 pokers, there is a button to trigger this function;
let tmpPoker = this.state.localPokers.pop();
if(this.state.localPokers.length <= 5){
let temp = shuffle([...allPokers]);
console.log('Temp_outside');
console.log(temp);
console.log("Temp_outside")
this.setState({
localPokers: temp,
},()=>{
tmpPoker = this.state.localPokers.pop();
console.log("Temp_inside");
console.log(temp);
console.log("Temp_inside");
this.setState({
localPokers: this.state.localPokers,
},()=>{
console.log("localPokers");
console.log(this.state.localPokers);
console.log("localPokers");
});
return tmpPoker
});
}
this.setState({
localPokers: this.state.localPokers
});
return tmpPoker
}
This is console output

You dont use the state inside setState like this, here is the correct way:
this.setState((state)=>({
localPokers: state.localPokers
}));

Related

React 2 random objects to appear constantly

I'm new with react and I'm building this game which has random objects appearing from holes. If you click on good object it adds score if you click a bad one it takes score away.
I've made 1 object to appear randomly and on click it will add score plus wont appear in same position. But when I try to add second it always pops in the same place and score stops working . Here is some code
clearFishes(){
for(let value in this.state){
if (!isNaN(value)){
this.setState({
[value]: 'translate(0, 110%)'
});
}
} }
displayFishes(){
let activeFish = Math.ceil(Math.random() * 12);
if (this.state.lastFish[0] === activeFish){
this.displayFishes();
return;
}
this.clearFishes();
this.setState({
[activeFish]: 'translate(0, 15%)',
lastFish: [activeFish]
}); }
lockOutClick(){
window.setTimeout(() => {
this.setState({ fishHasBeenClicked: false })
}, 350) }
addToScore(e){
if (this.state.fishHasBeenClicked){ return; }
let target = e.target;
target.parentNode.classList.add('game__cross');
target.classList.add('no-background');
this.lockOutClick();
this.setState({
background: '75px',
fishHasBeenClicked: true,
score: [parseInt(this.state.score, 0) + 1]
});
window.setTimeout(function(){
target.parentNode.classList.remove('game__cross');
target.classList.remove('no-background');
}, 500)}
Children elements should never access parent element directly. This approach is against React philosophy. You should read a little bit more of React philosophy.
target.parentNode.classList.add('game__cross');
target.classList.add('no-background');
Please use a method other than this direct approach. For example, storing a variable in useState.

Array empty even after a loop

I'm trying to fill an array with some registers from a database. However, even after retrieving said registers and place them into the array, after the loop the array stays empty
const userParties = [];
userFollowing.forEach(element => {
// Here's when I gather the information that I need
dispatchGetUserEvents(element.user, response => {
userParties.push(response);
console.log(userParties); // Here the array is full of elements, like it's supposed to be
});
});
console.log(userParties); // However, here 'userParties' return '[]'
this.setState({ followUsersEvents: userParties });
this.setState({ userImage: userImg });
I tried to update the state array on the loop, but I had no luck there either.
'userParties' is not a state array btw.
const userParties = [];
userFollowing.forEach(element => {
// Here's when I gather the information that I need
dispatchGetUserEvents(element.user, response => {
userParties.push(response);
console.log('dispatchGetUserEvents', userParties); // Here the array is full of elements, like it's supposed to be
});
});
console.log('outside', userParties); // However, here 'userParties' return '[]'
this.setState({ followUsersEvents: userParties });
this.setState({ userImage: userImg });
run this code and you will see that outside will be printed first and dispatchGetUserEvents later, as I mentioned in comments dispatchGetUserEvents is async function, so first will be executed console.log('outside', ...);

Updating arrays in react component state

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);});

Update nth item in array in state in parent from child in React?

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});
}

Why is concat on array inside this.state not working?

I'm trying to add rendered dates inside of my array located in my component state under the key date, but it always returns 0.
constructor(props) {
super(props);
this.state = {
dates: []
}
}
componentWillMount() {
this.renderDateComp()
}
renderDateComp() {
for(var i = 0; i < 5; i++) {
var dat = new Date(Date().valueOf());
dat.setDate(dat.getDate() + i);
this.setState({ dates: this.state.dates.concat(dat) });
console.log(this.state.dates); //prints length of 0
}
}
Well firstly, state transitions aren't immediate, they are asynchronous (in most circumstances). Think of it like every time you set the state, it adds that state change to a queue of changes and React is continuously working through that queue, doing all those state changes. So if you change the state, then on the next line of code immediately print the state, the state probably won't have changed because the console.log happens IMMEDIATELY after the state change is added to the list, but BEFORE the change has actually gone through that queue.
Secondly, it's probably better to set the state AFTER the for loop, so you only have to set it once, not 5 times.
The recommended way to do it would be to:
Call setState only once after you have the final dates array.
Log this.state.dates in the callback of setState (it will only be called after this.state has been properly updated by React.
The key point to note here is that setState is asynchronous. You probably do not need to console.log the array in your real code.
Read more about setState here.
constructor(props) {
super(props);
this.state = {
dates: []
}
}
componentWillMount() {
this.renderDateComp()
}
renderDateComp() {
var dates = this.state.dates;
for (var i = 0; i < 5; i++) {
var dat = new Date(Date().valueOf());
dat.setDate(dat.getDate() + i);
dates = dates.concat(dat);
}
this.setState({
dates
}, () => {
console.log(this.state.dates); //prints length of 5
});
}
It's worth reading the documentation of setState(). https://facebook.github.io/react/docs/react-component.html#setstate
It will queue the change, but there is a callback version if you want to use the new value somehow.
this.setState((prevState, props) => {
return {myInteger: prevState.myInteger + props.step};
});
More specifically, if you do this:
this.setState({ dates: this.state.dates.concat(dat) }, () => console.log(this.state.dates));
You'll get the result you're expecting.

Resources