One state lagging behind after setState - javascript

In my React component, I have a function which calls this.setState, and updates a few states. However, one of them is always one state "behind", and I suspect it's because of how it's called.
Here is the function in which the states are set:
handleReps(reps) {
var average = this.getAverage();
this.setState({
var1: CALCULATORS.epley(reps, this.state.weight),
var2: CALCULATORS.brzycki(reps, this.state.weight),
var3: CALCULATORS.lander(reps, this.state.weight),
// some more
average: average
});
}
Now, all of these update fine, and are changed in the DOM, but the average state is always one cycle behind. That is to say, it always shows the value that it should have had BEFORE the last change. I.e. the last time setState was called.
Are there some known factors that might be causing this delay on one of the states?
EDIT: Here is the getAverage() function.
getAverage () {
return CALCULATORS.average([
this.state.epley, this.state.brzycki, this.state.lander,
this.state.lombardi, this.state.mayhew, this.state.oconner,
this.state.wathen
])
}

The problem here was two-fold.
As Igorsvee accurately pointed out, I was calculating the average using old states. Rookie mistake on my part.
The problem however, persisted after I started using "new" states. At that point, I learned that the states aren't mutating immediately, so even if I had the correct order, it was still using one-cycle-old states.
This was solved by passing a callback as a second argument to the setState function. This callback is called AFTER mutation, and will use brand new values.

You use the current values from state to calculate the average, but at the same time you update the state with the new values.So,basically, you store the stale data for the average.I'd suggest to not store the calculated data, but rather invoke the method to calculate it, once you need it.

Related

how to properly update an object or array in react state [duplicate]

This question already has answers here:
Why can't I directly modify a component's state, really?
(7 answers)
Closed 3 years ago.
Assume this is my state:
state={
user:{
name: 'Joe',
condition:{
isPrivate: true,
premium: false
}
}
}
And this is the methods I can use to update user:
updateUser = (property, value)=>{
// firstway. probably not a good one
let user = this.state.user;
user[property] = value;
this.setState({user})
// second way. probably the best way
let user = JSON.parse(JSON.stringify(this.state.user))
user[property] = value;
this.setState({user})
}
Although I know modifying the state directly is not a good practice but I'm getting the same result from both of them with no side effects so far.
So why should I take this extra step to copy the state then modify it on the copied object while this slows down the operation (however so little)!
So which one would be faster? what would be the side effects of the first method in the context of react? and finally what are the pros and cons of each method?
In response to your first method of updating state, you are getting a reference to the object nested in your state.
let user = this.state.user;
user[property] = value;
In this chunk you have already updated the state, so you are actually performing a side effect. The call to setState() just reflects those changes in the UI(i.e. re-rendering of the component).
The reason for not modifying the state directly might be some unintentional updates in the state. For example, if you want to make an api call by modifying some of the data in this.state and sending it as the body of the request(Note that you don't want these updates to reflect in the UI), then modifying the state directly like you did in method 1 could cause some unwanted changes in the state and subsequent calls to setState() might expose some unwanted changes to the user of the application.
However in your example it's fine to use any of those methods but it might not be a good practice.
Hope this helps!
The basic idea is avoid mutating objects, create new objects instead.
This mantra means that you should avoid direct mutations to javascript objects that you have in memory but, instead, you should create a new object each time.
You can use the ES6 spread operator in order to get a clone of your object. With the spread operator you can also update the properties of the clone, so that you perform the required update to the object properties.
This is the code you need:
updateUser = (property, value) => {
const user = {...this.state.user, [property]: value}; // gets a clone of the current user and update its properties
this.setState({ user });
}
The three dots in the syntax above are not a typo, they are the aforementioned ES6 spread operator
Based on my knowledge (I'm quite new to react) there are basically three reasons to avoid direct state mutation:
recalculating a new state each time is simpler than trying to update an existing state. When I say simpler I mean simpler from a conceptual and coding perspective. Creating a new object each time avoiding any kind of side effect will simplify your code and will reduce your bugs.
you can't be sure on how your component and its children components are using a given piece of state. That piece of state is used by your component and could be passed to its children components via props. If you only reason on your component in isolation you can't know how the children components are using that piece of state. What's gonna happen when you mutate the object in memory by changing its properties ? The response is who knows. You can have a series of side effects and, more important, you cannot be sure about what kind of side effects you will get if you reason only on your component in isolation. It really depends on how the component hierarchy is composed. Reasoning about side effects is always a mess, it's too risky to cope with them and a better approach is trying to avoid them.
react and react dom have been designed to update the browser DOM efficiently when the state is mutated by following the best practices of functional approach (no side effects and no direct state mutation). This means that, if you use react the way you are suggested to, react itself will have a better time in calculating the modifications to be applied to the DOM in order to redraw your components and then your app will perform better.

Vue 2 mutating a data property inside a v-for causes infinite loops / problems

Although I could not find in the documentation, it appears that mutating a data array property isn't a good idea, even when it's not being rendered out to the view.
See this fiddle: https://jsfiddle.net/078v5142/3/
I need to decrement conditionalSet. I'm using pop() on every v-for loop to check the condition of an index. I can't use a computed property because i need to pass the index.
I can't just copy the conditionalSet array either because it needs to be tracked when a condition is set and popped.
This is a greatly simplified problem that I'm facing.
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js! I do not like loops, all that much.',
imageSet: [
'a','b','c','d','e','f','g', 'h', 'i', 'j'
],
conditionalSet: [1,2]
},
methods: {
doShow(index){
if(this.someInnerConditionThatsNotRenderedOut()){
if( index === 1 || index === 4 || index === 5 || index === 6){
this.conditionalSet.pop(); // <-- this is the problem, but how to I track?
return true;
}
}
console.log('I should no show index 5 and index 6')
},
someInnerConditionThatsNotRenderedOut(){
return true //comment this out. No error.
return this.conditionalSet.length > 0
}
}
})
The short answer
To cut a long story short, I think you can (and should) use a computed property for displaying the filtered list. I'm not 100% clear what you were trying to achieve in your example, but hopefully this fiddle gives you a pointer: https://jsfiddle.net/dtchqpjd/ If you look at the console when it runs, you'll be able to see how the logic pans out.
The longer answer
The problem is really caused by a combination of three things:
Your doShow function has a side-effect.
That side-effect is related to a data property, which is 'watched' by VueJS.
That data property is an array, which you need to 'get' in order to modify/pop.
VueJS has a reactivity system which watches data and, when it changes, determines the effect this will have on the DOM. This allows it to efficiently update the DOM in response to data changes, without needing to completely re-create the DOM every time something changes.
In this case, VueJS knows the following:
You get the conditionalSet property. You have to do this to call the pop method. But Vue doesn't know what you did with the value returned.
You change the conditionalSet property by calling the pop method.
Because you get and set the property, VueJS assumes that the result returned by the doShow function is stale, and hence re-evaluates it, and then you get an infinite loop. It can't see that you only 'got' it because you wanted to 'pop' it!
When you perform the filtering and modification all within a computed property, you avoid confusing VueJS (and the infinite loop) as it can see that the result is stable and doesn't need to be re-computed. It also happens to be much clearer code since the logic is all in one place. However, your computed property still has a side-effect which, if maybe if I understood more about what you were trying to do, could probably be avoided too.
A small illustration
To illustrate what's happening, here's a slight modification to your fiddle, where I've taken a separate (unknown to Vue) reference to the array. When I use that reference to call 'pop', you don't get the infinite loop: Vue can still see that we've changed the array, but because we didn't 'get' it, it doesn't assume that the output of the function is now stale. Uncomment the call which just 'gets' the array, and the problem comes back: Vue thinks it needs to re-evaluate the function.
Interestingly, Vue doesn't appear to look at the order of the dependencies: it doesn't matter if you 'get' then 'set', or 'set' then 'get'. It's just the fact that you did both which causes the circular dependency - they get added as dependencies and lead to a re-evaluation on the queue.
Commenting out the return true statement avoids the infinite loop simply because it prevents the array from being continually 'popped': once the array has been popped twice, your someInnerConditionThatsNotRenderedOut function returns false, and the potential for an infinite loop is cut short.
You just thought hard almost ;)
Imagine one computed method which filters your imageSet and returns which result you want and your v-for act on it.
Your fiddle with some changes:
https://jsfiddle.net/5dbakawb/

Observables with ngrx/store - race conditions

I have a problem that I believe is due to a combination of my initial state in ngrx-store and trying to get just the initial response from an observable. I'm using datatables, and large amounts of data for a static report - so I just want the first "real" response from:
Observable.zip(this.sites$, this.devices$, this.machines$, this.machineContent$)
In my ngrx reducer I have the initial state defined as [] for each of these. However, at times there is (what I think is a race condition) where one of those observables will be returned as [], but the Observable.zip resolves, and it incorrectly renders my table at that point.
I've tried various combinations of:
Observable.zip(this.sites$.skip(1), this.devices$.skip(1), this.machines$.skip(1), this.machineContent$.skip(1))
and
Observable.zip(this.sites$.skip(1), this.devices$.skip(1), this.machines$.skip(1), this.machineContent$.skip(1))
.take(1)
And these work about 80% of the time.
I also have an observable of 'isLoaded' for each of those observables (machinesIsLoaded, machineContentIsLoaded, etc.) which I thought about using with .takeUntil, except that I'd have to check for each of these to return true, and it feels like I must be doing something wrong.
Any ideas?
First: make sure your reducer is not ever mutating the array in state, because that will cause you problems. Make sure you are cloning the array or using an ImmutableArray.
Second: instead of filter, use skipWhile
function isEmpty(table: any[]) : boolean { return !table || !table.length; }
Observable.zip(this.sites$.skipWhile(isEmpty), this.devices$.skipWhile(isEmpty), this.machines$.skipWhile(isEmpty), this.machineContent$.skipWhile(isEmpty));
This will ignore results until the arrays get populated and then always use results even if the arrays empty again.

(React.js )Initial state generated randomly. How to prevent it from regenerate each time I handle an event?

I am building a game that players can attack each other by turn. So first I set the name, jobmanually and generate life,damage,magic randomly in componentWillMount().
I hope that every time I submit the attack form, certain amount of life with be reduced from the attacked person. But now every time I submit, the whole state is regenerated(with all kinds of bugs).
Can I do something to solve it?
app.js: https://ghostbin.com/paste/ype2y
attack.js: https://ghostbin.com/paste/wzm3m
I noticed that you do a lot of:
let players = this.state.players
which you are not supposed to do. Array is an object in js so here you are passing by reference. This means that every modification to the var players actually has side effects and modifies the state which you should never do. I generally recommend to never use in-place operations like splice, and to always use a copy of the state. In this case you can do:
let players = this.state.players.slice()
and from then on any modification to the players var does NOT affect the state. Double check you are not doing this anywhere else in your code. On top of that you should use the constructor only to set up and initiate your state. Otherwise every time the componentWillMount method is called your state is regenerated which is probably not the behavior you are expecting.
EDIT
I figured I could give you more pointers for what you are trying to do with arrays, as a general rule of thumb I follow this approach. If my new state has an array field which is a subset of the previous one then I use the .filter method, if the array of my new state needs to update some of its entries then I use the .map method. To give you an example on player deletion, I would have done it this way:
handleDeletePlayer(id) {
this.setState(prevState => ({
players: prevState.players.filter(player => player.id !== id)
}));
}
Your initial state should be generated in the constructor. This is done only once and will not be repeated when components props are updated.

knockout subscribe to observables needed?

I have a form with four input fields. I need to update the res (3) when one is updated from the UI. Meaning the one that is updated could be any of the four.
myViewModel.four.subscribe(function(newValue) {
//update one here
//update two here
//update three here
});
and so on.....
myViewModel.one.subscribe(function(newValue) {
//update two here
//update three here
//update four here
});
but how should I update the ones inside the method? To me it would look like a Stack Overflow situation...
In Knockout 1.2.1, if an observable is updated with exactly the same value, then updates will not be sent out. So, in that case you should be okay, if each of the values is updated with the same value.
Previously though, the best way to avoid a recursive loop of updates is to check in your subscribe if the observable really needs to be updated. This will cut the chain of updates.
So:
myViewModel.one.subscribe(function(newValue) {
if (two() !== "the right value") {
two("the right value");
}
//etc...
});
Otherwise, if there is more to your scenario, then there may be other options with writable dependentObservables that could be explored. When you get more than a two-way relationship though, manual subscriptions are usually the best bet.

Categories

Resources