# Clicking on a button stucks until page is finished rendering

**URL:** <https://discuss.emberjs.com/t/clicking-on-a-button-stucks-until-page-is-finished-rendering/18389>\
**Category:** Questions\
**Created:** [November 27, 2020, 5:48pm UTC](https://discuss.emberjs.com/t/clicking-on-a-button-stucks-until-page-is-finished-rendering/18389 "2020-11-27T17:48:42Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![saswatm95](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/saswatm95/32/17254_2.png) [@saswatm95](https://discuss.emberjs.com/u/saswatm95)\
**Post date:** [November 27, 2020, 5:48pm UTC](https://discuss.emberjs.com/t/clicking-on-a-button-stucks-until-page-is-finished-rendering/18389/1 "2020-11-27T17:48:43Z")

</div>

Hi guys,

Let’s say I have a page containing a button and a component and I want to modify the state of the component based on the click event of the button. The way I’m doing it is having a shared property in controller which gets updated on the button click event and is also passed onto the component as a parameter. Inside the component, didRecieveAttrs() calls some methods to update component state.

The problem is that the updation of component state inside didRecieveAttrs() takes a lot of time (some heavy computation involved). So, when I click on the button it gets stuck until the component is reloaded. Is there any way I can introduce a loading state to the component so that it’s not stuck on the click event.

---

<div class="post-metadata">

**Author:** ![sukima](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/sukima/32/5773_2.png) [@sukima](https://discuss.emberjs.com/u/sukima)\
**Post date:** [November 30, 2020, 11:57pm UTC](https://discuss.emberjs.com/t/clicking-on-a-button-stucks-until-page-is-finished-rendering/18389/2 "2020-11-30T23:57:16Z")

</div>

> [@saswatm95](#):
>
> Is there any way I can introduce a loading state to the component so that it’s not stuck on the click event

Absolutely. Have you seen [ember-concurrency](http://ember-concurrency.com/docs/introduction/)? It is uniquely suited for this very situation.

---

<div class="post-metadata">

**Author:** ![chriskrycho](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/chriskrycho/32/17763_2.png) [@chriskrycho](https://discuss.emberjs.com/u/chriskrycho)\
**Post date:** [December 1, 2020, 3:41am UTC](https://discuss.emberjs.com/t/clicking-on-a-button-stucks-until-page-is-finished-rendering/18389/3 "2020-12-01T03:41:42Z")

</div>

Additionally, if you want a lighter-weight solution that ember-concurrency (which is great, but a _lot_ of code! Tradeoffs!) you can use something like the `load` helper/utility and `AsyncData` described [in this post](https://v5.chriskrycho.com/journal/async-data-and-autotracking-in-ember-octane/) and available [here](https://gist.github.com/chriskrycho/306a82990dd82203073272e055df5cd1). You could just create a promise during the click that gets resolved, and tie the state of the button to whether the promise is in the `isLoading` state or is `isLoaded` or `isError`.

As an aside: as you start thinking about [Octane](https://emberjs.com/editions/octane/), it’s worth thinking about ways besides `didReceiveAttrs` to structure this kind of thing. One big upside of either Ember Concurrency or the `AsyncData` type is that you can just _derive_ the state you need from the arguments you pass to the component using getters and arguments, instead of trying to _set it_ using the lifecycle hooks.

To put that another way: whether using Ember Concurrency or that helper, you’ll want to think about modeling the behavior of the component as _derived_ from the arguments you pass into it. You can imagine doing something kind of like this:

```nohighlight
// app/controllers/some-controller.js
import Controller from '@ember/controller';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SomeController extends Controller {
  @tracked computationRunning = false;
  
  @action startComputation() {
    this.computationRunning = true;
  }
  
  @action finished() {
    this.computationRunning = false;
  }
}

```

```nohighlight
<!-- app/templates/some-controller.hbs -->
<button
  class={{if this.computationRunning "already-running"}}
  {{on "click" this.startComputation}}
>
  Start computation
</button>

<ComputationComponent
  @shouldDoComputation=this.computationRunning
  @onCompleteComputation=this.finished
/>

```

```nohighlight
// app/components/computation-component.js
import Component from '@glimmer/component';
import { load } from 'app/helpers/load';

function doTheCalculation(onCompletion) {
  return new Promise((resolve) => {
    // your long-running calculation...
    resolve(theValuesYouCalculate);
  }).finally(onCompletion);
}

export default class ComputationComponent extends Component {
  _previousResults;

  get results() {
    if (this.args.shouldDoComputation) {
      this._previousResults = load(
        doTheCalculation(this.args.onCompleteComputation)
      );
    }

    return this._previousResults;
  }
}

```

```nohighlight
<!-- app/components/computation-component.hbs -->
{{#if this.results.isLoaded}}
  {{this.results.value}}
{{else if this.results.isLoading}}
  <LoadingSpinner />
{{else if this.results.isError}}
  <p>Whoops, something went wrong, please try again!</p>
{{/if}}

```

Here’s how this works:

1. The only state we really have to track to make this work is a simple boolean property. If you have more complex state that the computation depends on, you can set that via the action as well, of course!
2. When you press the button, you update that root state, the boolean. When the computation finishes, it can signal to its parent that it is complete using the action passed in.
3. That would trigger an infinite loop if we did it synchronously, but using a `Promise` (and the `load` utility which creates an `AsyncData` for the `Promise`) means it _won’t_ happen synchronously, even if your computation was instantaneous, so this is safe.
4. The `#previousResults` private class field caches the results of the computation. This lets you just return the previous value you computed once you set the flag for whether the computation is running back to `false`, and run a _new_ computation whenever it is set to true again.
5. The `AsyncData` type returned by `load` provides `isLoading`, `isLoaded`, and `isError` values so that your template can present something useful while the `Promise` is resolving or if it rejects.

Note that for Ember Classic, you can make all of these exact same techniques work in the same way. You’ll use classic computed properties and Ember’s `set` function to set the root state instead, but the same principles work just fine!

---

<div class="post-metadata">

**Author:** ![saswatm95](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/saswatm95/32/17254_2.png) [@saswatm95](https://discuss.emberjs.com/u/saswatm95)\
**Post date:** [December 1, 2020, 4:10pm UTC](https://discuss.emberjs.com/t/clicking-on-a-button-stucks-until-page-is-finished-rendering/18389/4 "2020-12-01T16:10:29Z")

</div>

Thanks sukima. Will check out ember-concurrency.

---

<div class="post-metadata">

**Author:** ![saswatm95](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/saswatm95/32/17254_2.png) [@saswatm95](https://discuss.emberjs.com/u/saswatm95)\
**Post date:** [December 1, 2020, 4:29pm UTC](https://discuss.emberjs.com/t/clicking-on-a-button-stucks-until-page-is-finished-rendering/18389/5 "2020-12-01T16:29:33Z")

</div>

Thanks [chriskrycho](https://discuss.emberjs.com/u/chriskrycho). I think the crux here is that, you can leverage getter method being dependent on the controller property ( **shouldDoComputation** ), and let ember handle the re-rendering of template based on **IsLoading** property from the component. This is something that I was confused about, what I was doing instead is doing something similar but everything inside didRecieveAttrs(). Your approach looks much cleaner and concise.
