0

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.

alexandros84
  • 321
  • 4
  • 14

2 Answers2

3

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

}
Andrii Starusiev
  • 7,564
  • 2
  • 28
  • 37
  • I am trying it now. It looks very pro. Care to explain it a bit more? – alexandros84 Aug 01 '17 at 21:48
  • Unbelievable! It worked!! Are you setting a callback on setState? Excellent. ty. Care to explain why the previous formulation wasnt working despite of the console.log example at the end of the question working? – alexandros84 Aug 01 '17 at 21:54
  • 1
    @alexandros84, here are the [docs](https://facebook.github.io/react/docs/react-component.html#setstate) – Andrii Starusiev Aug 01 '17 at 22:47
  • 1
    And [this answer](https://stackoverflow.com/questions/42593202/why-calling-setstate-method-doesnt-mutate-the-state-immediately/42593250#42593250) should be helpful :) – Andrii Starusiev Aug 01 '17 at 22:56
1

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?

Wildhoney
  • 4,969
  • 33
  • 38
  • Please update the JSFiddle I've created. That's fine, but as you didn't specify the `render`, I just setup a `button` with `id=1`. – Wildhoney Aug 01 '17 at 21:34
  • Ty for the effort but the fiddle is composed only from one component. That is why you have a problem to understand what is going on. The idea is to have a list of two elements "a" and "b" with ids 1 and 2 resp. When you click on one of them the progress string adds this id on its state and every 3 clicks you are supposed to add this number id on your score. However despite of my best efforts the score is updating not when progress.length is 3 but when progress.length is 4... I tried to simplify a much more complex structure I am trying to create. – alexandros84 Aug 01 '17 at 21:38
  • I will try to build the case on your fiddle. The issue though must be on the way I am calling scoreUpdate() from handleClick(). – alexandros84 Aug 01 '17 at 21:41
  • I don't think `this.state.progress.length % 3 == 0` is doing what you think it's doing. `%` is for modulus. If you want to update `state.score` when the `state.progress` is `3` then isn't your logic: `this.state.progress.length === 3`? – Wildhoney Aug 01 '17 at 21:51