lit-element icon indicating copy to clipboard operation
lit-element copied to clipboard

Synchronous update

Open antonioaltamura opened this issue 3 years ago • 9 comments

I've got a code like this:

updated(changedProperties) {
        if (changedProperties.has("value")) {
            this.doubledvalue = this.value * 2;
            this.requestUpdate();
        }
    }
}

I want to trigger an update of the template after (and only after) I change this.doubledvalue. It seems, using updated() I update the template twice, asynchronously (update() as well is asynchronous). In my understanding internally lit-element might merge the update requests and perform just one, but also it might not (have I got it right?). Is there a way to make sure no update is being performed before the change of this.doubledvalue?

antonioaltamura avatar Nov 09 '20 10:11 antonioaltamura

updated() runs after an update, so any change of a reactive property or call to requestUpdate() will necessarily enqueue another asynchronous update. If you want to compute values before an update, you have to override update() instead, and do the work before super.update(...), like:

update(changedProperties) {
  if (changedProperties.has('value')) {
    this.doubledvalue = this.value * 2;
  }
  super.update(changedProperties);
}

justinfagnani avatar Nov 09 '20 17:11 justinfagnani

Looks like this approach needs to be documented at https://lit-element.polymer-project.org/guide/lifecycle#update

BTW, the code snippet above should be changed to use super.update instead of super.

web-padawan avatar Nov 09 '20 17:11 web-padawan

I could try it then, all the discussions here about synchronous update (https://github.com/Polymer/lit-element/issues/643, https://github.com/Polymer/lit-element/issues/365, and few others) maybe have misled me.

antonioaltamura avatar Nov 09 '20 17:11 antonioaltamura

@arthurevans It does look like we need to update the lifecycle docs to include info about when an update will be triggered. Specifically, we should discuss that derived properties can be computed in update before calling super and this will not trigger an additional update. However, setting properties after super.update or in firstUpdated/updated will trigger an additional update. Maybe this is documented somewhere but it seems reasonable to include in the lifecycle section.

sorvell avatar Nov 12 '20 19:11 sorvell

At some point (in #365) there were some mentions of a custom scheduler for chunked rendering. @sorvell could you elaborate a bit on that or give a basic example? I'm interested in rendering a large data array (stored as a property) as rows in a table and it seems that the delay between the DOM rendering in memory and the actual painting is several (5-10) seconds. The whole table is dumped at once on the screen after this delay. Any idea if it's possible to render each array item with requestAnimationFrame or in some other progressive manner? Thanks.

hadriann avatar Feb 08 '21 13:02 hadriann

@hadriann you might look at lit-virtualizer (https://github.com/PolymerLabs/uni-virtualizer/tree/master/packages/lit-virtualizer). It manages a virtual list, so it's not just chunked rendering, but it's designed for problems like the one you describe.

arthurevans avatar Feb 08 '21 17:02 arthurevans

@arthurevans thanks, that's an interesting project in and of itself - however it seems too much for what I need. What I'm trying to do is find a pattern that I can apply, rather than another library. Or, ideally, some built-in method to achieve that. I remember that the old Polymer library had a nice way to do just that with dom-repeat. From the old docs:

By default, dom-repeat tries to render all of the list items at once. If you try to use dom-repeat to render a very large list of items, the UI may freeze while it's rendering the list. If you encounter this problem, enable "chunked" rendering by setting initialCount. In chunked mode, dom-repeat renders initialCount items at first, then renders the rest of the items incrementally one chunk per animation frame. This lets the UI thread handle user input between chunks. You can keep track of how many items have been rendered with the renderedItemCount read-only property.

dom-repeat adjusts the number of items rendered in each chunk to try and maintain a target framerate. You can further tune rendering by setting targetFramerate.

That would be exactly what I need with lit-html and lit-element.

hadriann avatar Feb 09 '21 13:02 hadriann

@hadriann lit-virtualizer mostly is that for LitElement. We've decided to factor things to keep the core as small as possible w/ no keyed repeat at all. Then add keyed repeat with the repeat() directive, and for most other things jump straight to virtualization which addresses large data sets lists in a number of ways.

You could definitely write a directive that schedules slices of a list to render over time like dom-repeat. You can look at the asyncAppend directive which has most of what's needed, but renders each element as soon as it's available.

justinfagnani avatar Feb 09 '21 17:02 justinfagnani

@justinfagnani thanks, I managed to use asyncAppend to render each array item with a promisified requestAnimationFrame in the generator. The only caveat is that if the data array is a LitElement property it keeps getting re-rendered when other properties change (even if the array itself doesn't change). The solution was to make use of the guard directive as well, and it seems ok so far.

hadriann avatar Feb 10 '21 14:02 hadriann

Closing as these use cases should be covered by willUpdate and scheduleUpdate which are documented here: https://lit.dev/docs/components/lifecycle/#reactive-update-cycle and https://lit.dev/docs/api/ReactiveElement/#ReactiveElement.scheduleUpdate.

sorvell avatar Sep 08 '22 21:09 sorvell