Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setState( obj, callback) - callback called before state has mutated #6455

Closed
wdiechmann opened this issue Apr 8, 2016 · 9 comments
Closed

Comments

@wdiechmann
Copy link

I am fighting what used to be called 'a race condition' - I'm not sure what you guys call it in the React world :)

calling this snippet in a component

  reportArgs: (e) =>
    console.log( 'later: ' + @state.args)

  sendPrintRequest: (e) =>
    w = @state.url
    arg = @state.args
    arg = "komplex string manipulations goes here"
    @setState {wurl: w, args: arg}, console.log 'sooner ' + @state.args
    setTimeout @reportArgs, 5000

proves my point - I trust.

Either it is a 'noob observation - go do this instead' or a documentation glitch (as the docs says that the callback is called once the state is mutated) or, eventually, a bug

Obviously I'm rooting for #1 which would have me on my way in a jiffy - providing someone with a lot more knowledge of React would "stick it to me" so to speak :)

cheers,
Walther

@RaitoBezarius
Copy link
Contributor

@wdiechmann Could you explain why do you want to report the state?
If I try to understand, you're logging in the console the state once it has been updated.
And trigger a callback to expire in 5 seconds and log them.

I don't see any race condition?

@syranide
Copy link
Contributor

syranide commented Apr 8, 2016

@setState {wurl: w, args: arg}, console.log 'sooner ' + @state.args

Does not create a setState-call with a callback that calls console.log, it actually calls setState, then calls console.log and returns the result of console.log as the callback for setState (which is obviously not a calback). So you're doing setState(null, console.log()) but what you want is the equivalent of setState(null, function() { console.log() }). I don't know CS so I can't really help you with the syntax, but that's the problem.

@syranide syranide closed this as completed Apr 8, 2016
@wdiechmann
Copy link
Author

no @syranide - it's not the problem! The state does get updated - eventually

but anyways - I'll get by

(and @RaitoBezarius - I'm setting state and depending on it in a few instructions later - that's why) ;)

the code provided is merely to demo the effect :)

@syranide
Copy link
Contributor

syranide commented Apr 8, 2016

@wdiechmann Yes, the state gets updated, but your console.log is effectively called before setState is even called.

@setState {wurl: w, args: arg}, console.log 'sooner ' + @state.args

transpiles to

this.setState({
  wurl: w,
  args: arg
}, console.log('sooner ' + this.state.args));

which is equivalent to

var r = console.log('sooner ' + this.state.args);
this.setState({
  wurl: w,
  args: arg
}, r);

or just

console.log('sooner ' + this.state.args);
this.setState({
  wurl: w,
  args: arg
});

@syranide
Copy link
Contributor

syranide commented Apr 8, 2016

https://jsfiddle.net/6zed7uuh/ (click on the div) shows the expected behavior, or are you expecting something else?

@wdiechmann
Copy link
Author

hmmm - I see the 'fiddle' - but this snippet does not show the expected behaviour -


    PrintPrompt.prototype.reportArgs = function (counter) {
      return console.log("later: (" + counter + " " + this.state.args + ") \n");
    };

    PrintPrompt.prototype.sendPrintRequest = function (e) {
      var arg, w, wrl;
      wrl = this.state.url;
      arg = this.state.args;
      if (wrl === 'href') {
        wrl = window.location.href.replace(/#!$/, "");
      }
      if (wrl.match(/\?/)) {
        w = wrl.split("?")[0] + "/print";
        arg = wrl.split("?")[1] + "&";
      } else {
        w = wrl + "/print";
      }
      arg = arg + "print_format=" + this.state.print_format + "&print_template_key=" + this.state.print_template_key + "&paper=" + this.state.print_paper;
      this.setState({
        wurl: w,
        args: arg
      }, this.reportArgs(1));
    };

I cannot explain the difference - but like I replied - no big deal, I'll just have to setTimer and wait for the enqueuedCallback to finish :)

thx for sharing anyway!

cheers,
walther

@wdiechmann
Copy link
Author

Somehow a 'scope' change is enough - or whatever it is called - try this for measure:


    PrintPrompt.prototype.reportArgs = function (counter) {
      return console.log("later: (" + counter + " " + this.state.args + ") \n");
    };

    PrintPrompt.prototype.sendPrintRequest = function (e) {
      var arg, w, wrl;
      wrl = this.state.url;
      arg = this.state.args;
      if (wrl === 'href') {
        wrl = window.location.href.replace(/#!$/, "");
      }
      if (wrl.match(/\?/)) {
        w = wrl.split("?")[0] + "/print";
        arg = wrl.split("?")[1] + "&";
      } else {
        w = wrl + "/print";
      }
      arg = arg + "print_format=" + this.state.print_format + "&print_template_key=" + this.state.print_template_key + "&paper=" + this.state.print_paper;
      this.setState({
        wurl: w,
        args: arg
      }, this.reportArgs(1));
      setTimeout(this.reportArgs, 0, 0);
      console.log('later 2' + this.state.args);
      console.log('later 3' + this.state.args);
      console.log('later 4' + this.state.args);
      console.log('later 5' + this.state.args);
      this.reportArgs(6);
      this.reportArgs(7);
      this.reportArgs(8);
      this.reportArgs(9);
      this.reportArgs(10);
      this.reportArgs(11);
      this.reportArgs(12);
      this.reportArgs(13);
      this.reportArgs(14);
      this.reportArgs(15);
      this.reportArgs(16);
      this.reportArgs(17);
      return this.form = $('#decide_print_output form');
    };

weird, right?!

@syranide
Copy link
Contributor

syranide commented Apr 8, 2016

@wdiechmann Your problem is explained in my comments above, you're not providing a callback to setState, you're calling the function and returning the value to be used as a callback (which it isn't). Two wildly different things.

@wdiechmann
Copy link
Author

waooo - !!

so a lambda like this

    @setState {wurl: w, args: arg}, () ->
      console.log @state.args

works -

I'm not sure I quite see the difference - calling the function or asking setTimer to call it or asking eval to do it inline (the lambda thing) - of which the latter two works -

thx again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants