I am having trouble understanding why my component state does not change inside the for-loop.
Here's an example:
class Example extends React.Component {
constructor() {
super()
this.state = {
labelCounter: 1,
}
}
componentDidMount() {
for (let i = 0; i < 10; i++) {
this.setState({ labelCounter: this.state.labelCounter + 1 })
console.log(this.state.labelCounter) // this.statelabelCounter = 1
}
}
}
Whereas if I changed the code slightly to this, it seems to be changing as expected:
class Example extends React.Component {
constructor() {
super()
this.state = {
labelCounter: 1,
}
}
componentDidMount() {
for (let i = 0; i < 10; i++) {
this.setState({ labelCounter: ++this.state.labelCounter })
console.log(this.state.labelCounter)
}
}
}
I think the issue you're having is that react batches updates to the state. This means that instead of it working synchronously, it just applies setState({ labelCounter: this.state.labelCounter + 1}) after the loop, and this.state.labelCounter + 1 is resolved to a fixed number (1 in this case), that is reapplied 10 times. So the labelCounter is set to 1 10 times.
In the last example you are updating by changing the property yourself (and not having react do it), which is why it works.
I would guess the best way to do it is to wait for a batch to have been applied (for example with setTimeout(x, 0)) and doing the rest after that, or trying to avoid this altogether.
Correct me if I am wrong, but "this.state.labelCounter + 1" is not the same as "this.state.labelCounter ++" because it is evaluating the state value before making the changes to the state value.
Another option would be "this.state.labelCounter += 1"
Source:
(Not exactly similar)
Difference between ++ and +=1 in javascript
Related
I'm trying to periodically run a calculation (every 5 seconds) and update a component's state with the calculated value using a setInterval timer. What I've seen is that the updateCalculation() function does get called every 5 seconds but when monitoring the memory usage using the Chrome devtools it just keeps on growing endlessly on every call by setInterval. The memory never seems to get released.
Snapshot 1:
Snapshot 2:
What could be a possible workaround for running calculations periodically?
I'm still pretty new to React and am really not sure what I'm doing wrong.
class MyComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
calculated: []
};
}
componentDidMount() {
this.calculationUpdater = setInterval(() => this.updateCalculation(), 5000);
}
componentWillUnmount() {
clearInterval(this.calculationUpdater);
}
// Memory leak here
// The function that gets called by setInterval to calculate data and update the state
updateCalculation() {
let data = [];
for (let i = 0; i < 60000; i++) {
data.push({x: i, y: i, z: i});
}
this.setState({
calculated: data
});
}
render() {
return (
<React.Fragment>
<Child calc={this.state.calculated} />
</React.Fragment>
);
}
}
I'm not doing anything special with the Child component at the moment. This is what it looks like:
class Child extends React.PureComponent {
render() {
return (
<div></div>
);
}
}
EDIT: Check following post: Does JavaScript setInterval() method cause memory leak?
You are not clearing the interval because you are are not setting or reading state correctly. So if your component keep getting mounted and unmounted, you set a new interval but do not clear the interval on unmount.
this.calculationUpdater = setInterval(() => this.updateCalculation(), 5000);
This should be
const calculationUpdater = setInterval(() => this.updateCalculation(), 5000);
console.log(calculationUpdater )
this.setState({calculationUpdater : calculationUpdater})
And you access the state as following:
console.log(this.state.calculationUpdater);
clearInterval(this.state.calculationUpdater);
The only thing I can suggest is to try switching from PureComponents to Components; I noticed my initial snapshot was laggier for PureComponents, though either way my results were not like yours.
How can you change the status as an object? I did it this way, but I don't think it's right, because in compiling it gives a setState warn.
I would like to understand how to change a state whose value is an object.
class Animation extends React.Component {
constructor(props) {
super(props);
this.state = {
step : step,
ledge :{zoom:1, display:"flex"},
map :{zoom:0, x:0, y:0, rotate:0},
};
}
StepForward = () => {
step = step + 1;
//STEP 1
if(step === 1){
this.setState({
...this.state.ledge.zoom = this.state.ledge.zoom - 1,
...this.state.ledge.display = "none"
});
}
}
StepBack = () => {
step = step - 1;
if(step < 0 ){
this.setState({step:step})
step = -1
}
//STEP 0
if(step === 0){
this.setState({
...this.state.ledge.zoom = this.state.ledge.zoom + 1,
...this.state.ledge.display = "flex",});
}
}
render() {
return (
<div id="content_animation">
<div id="step back" class="command" onClick={this.StepBack}>
<img class="arrow" src="img/arrow_b.svg" alt="back"/>
</div>
<div id="animation">
<AnimationStep
step = {this.state.step}
ledge={this.state.ledge}
map={this.state.map}/>
</div>
<div id="step forward" class="command" onClick={this.StepForward}>
<img class="arrow" src="img/arrow_f.svg" alt="forward"/>
</div>
</div>
)
}
}
export default Animation
when I compile it gives me the error that you see below but if you insert a comment above the line of code "wrong", then it works and compiles correctly ...
Compiled with warnings.
Do not mutate state directly. Use setState() react/no-direct-mutation-state
Do not mutate state directly. Use setState() react/no-direct-mutation-state
Do not mutate state directly. Use setState() react/no-direct-mutation-state
Do not mutate state directly. Use setState() react/no-direct-mutation-state
Search for the keywords to learn more about each warning.
To ignore, add //eslint-disable-next-line to the line before.
As the error clearly states, you're mutating your state directly, which will cause bugs along the way. You can set state without mutating it like this:
this.setState(state => ({
...state,
ledge: {
...state.ledge,
zoom: state.ledge.zoom - 1,
display: 'none'
}
}));
Useful links:
Mutating state - https://daveceddia.com/why-not-modify-react-state-directly/
Functional setState - https://www.freecodecamp.org/news/functional-setstate-is-the-future-of-react-374f30401b6b/
Object spreading - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals
The problem is with your setState calls and how you're setting those values:
this.setState({
ledge: {
zoom: this.state.ledge.zoom - 1,
display: 'none'
}
});
Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
You perhaps would love to check difference between mutable and immutable aswell.
What you should do in this case you could use spread operator :
this.setState(prevState => ({
ledge: { // object that we want to update
...prevState.ledge, // keep all other key-value pairs
zoom: this.state.ledge.zoom -1 // update the value of specific key
}
}))
When you use this.setState, you don't have to explicitly say this.state in the assignments within it. I haven't been able to set the inner class the way you or the other answers have done. I usually do another step to successfully set the state.
this.setState({
...this.state.ledge.zoom = this.state.ledge.zoom + 1,
...this.state.ledge.display = "flex",});
becomes:
var tempLedge = this.state.ledge;
tempLedge.zoom = this.state.ledge.zoom + 1;
tempLedge.display = "flex";
this.setState({ledge = tempLedge});
Related, but not a dupe:
What is the correct way of setting state variables in Reactjs and what is the difference between these approaches?
Also, check out this link for more info: State and Lifecycle
This questions is actually React JS related. Is it OK to define internal class variables inside one of the class methods and then use it in other methods? I mean to do something like this:
class Something extends React.Component {
state = {
value: 'doesnt matter'
};
something = () => {
//a lot is happening in here and at some point I define this.thing
this.thing = 1;
}
increase = () => {
if(this.thing) {
this.thing += 1;
}
}
decrease = () => {
if(this.thing && this.thing > 0) {
this.thing -= 1;
}
}
render() {
return (
<span>this.state.value</span>
);
}
}
thing is that I don't need to put that this.thing as a state value, because I only need it internally. Please be aware that this code is just an example, real code is a bit more complicated, but the main question, is it OK to define class internal variables(this.thing) like I do in my example? Or maybe I should do this differently? What would be the best practice?
It's not a problem to use the constructor to do such a thing but based on the react theory and UI rendering this kind of usage will not re-render or follow the react pattern of trigger and re-render, it will just server as a storage for a value that has nothing to do with the react life cycle.
class Something extends React.Component {
constructor(props) {
super(props);
// Your thing
this.thing = 0;
this.state = {
value: "doesnt matter."
};
}
something = () => {
//a lot is happening in here and at some point I define this.thing
this.thing = 1;
};
increase = () => {
if (this.thing) {
this.thing += 1;
}
};
decrease = () => {
if (this.thing && this.thing > 0) {
this.thing -= 1;
}
};
render() {
this.something();
console.log(this.thing); // will equal 1.
return <span>{this.state.value}</span>;
}
}
I don't need to put that this.thing as a state value, because I only need it internally.
A React component's state should also only be used internally.
What would be the best practice?
You can use instance variables (ivars) instead of state to increase performance because you may reduce the burden on the event queue. Aesthetically, ivars often require less code. But state updates are usually preferred because they will trigger a re-render; this guarantee makes your code easier to think about, as the render is never stale. In your case, the render function is independent of this.thing, so it's okay to store it in an ivar.
Generally, it's best to initialize ivars in the constructor because it runs first, so this.thing is guaranteed to be ready for consumption by other methods:
constructor(props) {
super(props);
this.thing = 0;
}
I've got this React parent component here. The children components at this point are just returning dropdown menus. I expected that componentWillReceiveProps would update the state here, which in turn should be passed to StopList as props. However, when state.selectedSub is changed through handleSubSelect, nothing happens and StopList doesn't receive any props.
Is my mistake with the asynchronous nature of componentWillReceiveProps? Is it in the wrong place in my code? Am I using the wrong lifecycle method?
// We're controlling all of our state here and using children
// components only to return lists and handle AJAX calls.
import React, { Component } from 'react';
import SubList from './SubList';
import StopList from './StopList';
class SubCheck extends Component {
constructor (props) {
super(props);
this.state = {
selectedSub: '--',
selectedStop: null,
stops: ['--'],
};
this.handleSubSelect.bind(this);
this.handleStopSelect.bind(this);
}
// We want the user to be able to select their specific subway
// stop, so obviously a different array of stops needs to be
// loaded for each subway. We're getting those from utils/stops.json.
componentWillReceiveProps(nextProps) {
var stopData = require('../utils/stops');
var stopsArray = [];
var newSub = nextProps.selectedSub
for(var i = 0; i < stopData.length; i++) {
var stop = stopData[i];
if (stop.stop_id.charAt(0) === this.state.selectedSub) {
stopsArray.push(stop.stop_name);
}
}
if (stopsArray.length !== 0 && newSub !== this.state.selectedSub) {
this.setState({stops: stopsArray});
}
}
handleSubSelect(event) {
this.setState({selectedSub:event.target.selectedSub});
}
handleStopSelect(event) {
this.setState({selectedStop:event.target.selectedStop})
}
render() {
return (
<div>
<SubList onSubSelect={this.handleSubSelect.bind(this)}/>
<StopList stops={this.state.stops} onStopSelect={this.handleStopSelect.bind(this)}/>
</div>
);
}
}
export default SubCheck;
You are duplicating data, and causing yourself headaches that aren't necessary.
Both selectedSub and selectedStop are being stored as props and as state attributes. You need to decide where this data lives and put it in a singular location.
The problem you are encountering entirely revolves round the fact that you are changing the state attribute and expecting this to trigger a change to your props. Just because they share a name does not mean they are the same value.
I am experimenting with js + React and I am facing an unexpected behavior:
In the following example, while it seems to work fine at first I do not get a score change when (this.state.progress.length%3==0) as expected.
The progress string seems to be updating nicely but the score updates every fourth click...
Edit: I should pin-point the source of the issue because ppl are busy, the problem is the way the handleClick() on the child component interacts (calls) the scoreUpdate() from the same component. However I do not think the solution is trivial because the consol.log() example at the end of the question works.
There is obviously an issue on the way I am organizing my code, but what?
Should I be using Promises to call my scoreUpdate() function?
Or is there a better way to go around this?
Child component:
import React from 'react';
export class Child extends React.Component {
constructor(props) {
super(props);
this.state = { progress: "0",
score: 0};
this.handleClick = this.handleClick.bind(this);
this.scoreUpdate = this.scoreUpdate.bind(this);
}
handleClick(e) {
let previous = this.state.progress;
let score = Number(e.currentTarget.id);
this.setState({progress: previous+e.currentTarget.id});
this.scoreUpdate(score);
}
scoreUpdate(score) {
if (this.state.progress.length%3==0) {
let previous = this.state.score;
this.setState({score: previous+score}); }
}
render() {
return (
<div>
<ul>
<li id="1" onClick={this.handleClick}>a</li>
<li id="2" onClick={this.handleClick}>b</li>
</ul>
<p>progress</p>
<p>{this.state.progress}</p>
<p>progress length</p>
<p>{this.state.progress.length}</p>
<p>score</p>
<p>{this.state.score}</p>
</div>
);
}
}
Parent component:
import React from 'react';
import ReactDOM from 'react-dom';
import {Child} from './components/Child';
class Parent extends React.Component {
render() {
return (
<Child />
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('app')
);
Any valid insight / explanation on why is this hapening would be highly appreciated. What puzzles me is that when I type in the console:
var b = 1;
function c() {
b=b+2;
d();
}
function d() {
console.log(b);
}
c();
This returns 3 as expected.
If you know this question to have a duplicate please leave a comment in order for me to remove it.
Try like this:
handleClick(e) {
let previous = this.state.progress;
let score = Number(e.currentTarget.id);
this.setState({progress: previous+e.currentTarget.id}, () => this.scoreUpdate(score));
}
scoreUpdate(score) {
if (this.state.progress.length%3==0) {
let previous = this.state.score;
this.setState({score: previous+score}); }
}
I've setup a JSFiddle for your component, but I still have absolutely no idea what's happening. Your state.progress appears to be a string concatenation of the event.target's id attribute: 0111 for instance.
Thus each time scoreUpdate is invoked, it adds the id (which in the JSFiddle's case is always 1) attribute to the end:
Click 1: state.progress === 0
Click 2: state.progress === 01
Click 3: state.progress === 011
Click 4: state.progress === 0111
Only on the fourth click does this.state.progress.length % 3 == 0 yield true, and therefore update state.score.
Please elucidate?