meteor / react-packages

Meteor packages for a great React developer experience
http://guide.meteor.com/react.html
Other
574 stars 159 forks source link

Tracker.autorun not running reactively inside of componentDidMount #99

Closed nickredmark closed 3 years ago

nickredmark commented 9 years ago

If in a react class I add

Tracker.autorun ->
    console.log Session.get "foo"

to the componentDidMount method, the autorun method runs only once, but not each time the "foo" variable gets updated. The very same two lines of code achieve the desired result if I add them inside of meteor.startup

meteorfan commented 9 years ago

Bump. Another case would be to run a method depending on @state

Tracker.autorun ->
    console.log @state.foo
froatsnook commented 9 years ago

I believe this has something to do with react-template-helper. I was trying to migrate from code taken from reactjs:react (which lets you do {{> Component}} instead of {{> React component=Component}}) to react-template-helper when it broke. I was using the same react-runtime in both cases.

@nicolamr, @meteorfan: are you also using react-template-helper?

nickredmark commented 9 years ago

@froatsnook thanks for your answer. Unfortunately that's not the case. I did have react-template-helper installed, which I now removed since I'm not using blaze anymore. Yet the problem persists. @meteorfan is just another account of mine, sorry for the confusion.

So you are saying that it works in your case? I wasn't sure Tracker.autorun is even supposed to work inside of a react component.

froatsnook commented 9 years ago

I did more research and it's even more confusing. Tracker.autorun works for me inside componentWillMount always when using {{> Component}} (from reactjs:react) and sometimes when using {{> React component=Component}} from react-template-helper. Maybe it has to do with how they're nested? I'll try to make a repro.

I hope to get it working with react-template-helper! I like using it in componentWillMount because it gives me control over when I want to update state.

ffxsam commented 8 years ago

Same issue for me:

https://forums.meteor.com/t/what-would-cause-a-tracker-computation-to-stop/10431

ffxsam commented 8 years ago

Full componentDidMount code:

  componentDidMount() {
    let p = this.props;
    let computation;
    let uploader = new Slingshot.Upload('audioUpload');

    uploader.send(p.file, function (error, url) {
      computation && computation.stop();

      if (error) {
        console.error(error);
      } else {
        console.log('done!');
      }
    });

    computation = Tracker.autorun(() => {
      console.log('upload progress =', uploader.progress());
      this.setState({progress: Math.ceil((uploader.progress() || 0) * 100)},
        () => {
          Dispatcher.dispatch('FILE_UPLOAD_PROGRESS', {
            id: p.id,
            progress: this.state.progress
          });
        });
    });

    computation.onInvalidate(function () {
      console.log('we invalidated');
    });
    computation.onStop(function () {
      console.log('why did we stop?!');
    });
  }

And of course, when I initiate a file upload, right away in the console I see "we invalidated" followed by "why did we stop?!" and then a few seconds later, the file successfully finishes uploading.

This isn't terribly crucial for me, as I've temporarily instated a setInterval function to update the progress every 500ms. But it would be great if someone from MDG could look into this.

nickredmark commented 8 years ago

Bump. Is there anyone for which this is not an issue?

ffxsam commented 8 years ago

Since no one's pinged anyone from MDG yet, I'll go ahead and use that card now.

I SUMMON.. @stubailo! ;) Sashko, can you speak to this at all? Is Tracker.autorun not meant to be used within componentDidMount for some reason?

mikecardwell commented 8 years ago

FWIW, I am having this exact problem also.

stubailo commented 8 years ago

Personally, I think this is a bug - you should be able to use it from then. As a workaround, you might be able to use setTimeout:

componentDidMount() {
  setTimeout(this.startComputation, 0);
},

startComputation() {
  Tracker.autorun(...);
}

Let me know if this works - I haven't tried running the code. But we should fix this bug since running an autorun from componentDidMount is super reasonable. @yyx990803, we should put this on the radar since it will be a gotcha with many tracker-based React data loaders.

nathanwebb commented 8 years ago

Thanks @stubailo - this caught me out for a couple of hours today! The solution you posted works perfectly.

stubailo commented 8 years ago

@nathanwebb make sure you also stop the computation from componentWillUnmount - otherwise you might end up with a memory leak.

nathanwebb commented 8 years ago

Brilliant - thanks for the tip

On 3 March 2016 at 19:22, Sashko Stubailo notifications@github.com wrote:

@nathanwebb https://github.com/nathanwebb make sure you also stop the computation from componentWillUnmount - otherwise you might end up with a memory leak.

— Reply to this email directly or view it on GitHub https://github.com/meteor/react-packages/issues/99#issuecomment-191648907 .

ckiely91 commented 8 years ago

@stubailo, thanks so much for the workaround. I spent a big chunk of yesterday trying to figure this one out. Works perfectly now!

PEM-- commented 8 years ago

Wow nasty one. Thanks for the circumvention.

dohomi commented 8 years ago

@benjamn @stubailo as we experience similar issues with angular-meteor-auth package and the initial Tracker.autorun for Meteor.isLoggingInit would be great if you could look into this issue again. It would be a great help if there is a debug log for the computation for being able which function is actually responsible to stop the computation.

dohomi commented 8 years ago

Is there anybody having an eye on this issue? On a large app with Meteor + Angular this issue is driving you crazy. Having no reliability on a subscribe or autorun to run properly is breaking the magic of Meteor. Any traceable debug information would be great. This issue was rising especially since the release of Meteor 1.3.x.

nickredmark commented 8 years ago

Just wanted to say that I don't use Tracker.autorun inside of a component since I started using react-komposer, see https://github.com/kadirahq/react-komposer

shawnmclean commented 8 years ago

Could someone explain to me why this workaround works? I'm calling Tracker.autorun through a redux dispatcher, which should be asynchronous, so how does this initial call inside componentDidMount affect the computation inside an async method?

samylaumonier commented 8 years ago

Any news about this bug?

ffxsam commented 8 years ago

After much time has passed and more knowledge has been gained, it makes sense to NOT have any Meteor or reactive stuff inside the component, but rather do that in the container instead (created via createContainer) and pass props into the React component.

erickrojasfigueroa commented 6 years ago

It works very well

lchangdev commented 6 years ago

2018 and this is still not fixed wow

dburles commented 6 years ago

@lchangdev use withTracker from react-meteor-data package

karlitos commented 6 years ago

I assume I would get in the same problems when using Cursor.observeChanges but I could not find any example of how to use withTracker with observeChanges.

nickredmark commented 6 years ago

I'm going to unsub since 3 years later I haven't been using meteor in a while. I'm happy using next.js + graphql + ooth for the accounts system.

rooch84 commented 5 years ago

In general, I use withTracker. But I have a special use case where I want to update some CSS without re-rendering, so I need to use Tracker.autorun inside the component.

The setTimeout workaround works for me, so I'll use that for now.

rooch84 commented 5 years ago

UPDATE: I've found instances where setTimeout(this.startComputation, 0); does not run reliably. I've increased the delay to 1000ms and it works, but this is not ideal. It seems that the difference between a reactive computation and non-reactive computation is the number of elements on _onInvalidateCallbacks (one is non-reactive, three is reactive). As a workaround, I've found the following works:

this.autorunManager= null;

componentDidMount() {
  setTimeout(this.startComputation, 10);
},

startComputation() {
  this.autorunManager =  Tracker.autorun(...);
  if (this.autorunManager._onInvalidateCallbacks.length < 3) {
      setTimeout(this.startComputation, 10);
    }
}
filipenevola commented 3 years ago

I'm closing this just because it's too old. We can open new issues for items that are still valid.

Most of these items were solved already or replaced by new strategies (like hooks)