lit: Values changed outside of lit-html rendering are not updated from lit-html

Since lit-html stores previously rendered values for dirty-checking, any value changed from outside lit-html will not be overwritten in another render if the value passed to lit-html is the same.

See this live example: https://stackblitz.com/edit/lit-html-rt4n6e

This is actually working as intended, as it’s intended that lit-html maintains control over bound values. But this issue has come up a few times in properties that are affected by user actions:

  • HTMLInputElement.value
  • Element.scrollTop

Currently the recommended solution would be to add event listeners which update the bound value so that they’re in sync. That’s non-obvious though, and we should handle this automatically if we can.

One option is to stop storing the previous value in AttributePart, PropertyPart, etc., and instead use the current bound attribute/property value instead as the source of truth.

There are two potential downsides:

  • It might be slower, especially for native DOM properties. Crossing DOM bindings just to see if a value should be updated might slow things down.
  • Values may be modified by the setter. For instance, e.className = ['a', 'b'] results in a className of a,b. This would defeat the dirty check and cause it to be set every render.

Another solution is to vend a directive that defers to the underlying property specifically for these cases:

import {live} from 'lit-html/directives/live.js';
// ...
html`<input .value=${live(value)}`>`;
// or
html`<div .scrollTop=${live(top)}`></div>`;

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 11
  • Comments: 29 (13 by maintainers)

Commits related to this issue

Most upvoted comments

Thanks for this Justin. May I ask… can you ask the documentation guys to actually document it? At the moment there is no sign of it in https://lit-html.polymer-project.org/guide/template-reference and I think it’s a very useful directive in some common cases

On Thu, 16 Jan 2020 at 07:47, Justin Fagnani notifications@github.com wrote:

Closed #877 https://github.com/Polymer/lit-html/issues/877 via #1057 https://github.com/Polymer/lit-html/pull/1057.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Polymer/lit-html/issues/877?email_source=notifications&email_token=AAQHWXSSADMKHFYS6NZUG5TQ56OCFA5CNFSM4HASYF4KYY3PNVWWK3TUL52HS4DFWZEXG43VMVCXMZLOORHG65DJMZUWGYLUNFXW5KTDN5WW2ZLOORPWSZGOV77MFLI#event-2952708781, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAQHWXTZMQ5XZ5ISWE4GZPTQ56OCFANCNFSM4HASYF4A .

I am about to hit this problem. @justinfagnani do you have something “official” in the works? It will avoid me wasting time on my own (often half broken) solution…

That work around is correct, and is a somewhat heavy-handed but totally valid approach to getting the desired outcome.

The real problem is that the model (what lit-html thinks is the value of the input) and the reality (the actual state of the input) is not the same. This happens when “a radio button is set to checked outside lit-html” and this change is not correctly synced back into the model. If lit-html thinks the checked property was false and still is false, it won’t bother setting it to false. This is good, because it cuts down on useless operations, but if your checked property changed to true outside the knowledge of lit-html, then it is obviously bad, because the property should be set to false again.

The “real” solution to fixing this problem is by making sure your input state is always synced with what lit-html thinks, but this can be very cumbersome and sometimes hard do correctly. In those cases, a simple solution like the above can be appropriate.

I will try my hand at implementing a live directive over the weekend, which is an alternative to this force directive, but in the grand scheme of things I expect the performance difference between these to not be very significant.

I think directive way more clearify solution, but less intuitive for beginers.

Anyway, i prefer directive.

This happens with the checked attribute on certain input types as well.