lit / lit/lit

Wrapper triggers the setter on each re-render

Open
#5,009 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
21.8k
Forks
1.1k
Avg merge
18h 25m
Merged PRs (30d)
2

Description

### Which package(s) are affected?

React (@lit/react)

### Description

Lit React wrapper triggers the web component setter on each re-render even if the value did not change since the last render.
In comparison, without wrappers, both React 18 and React 19 only re-trigger the web component setter if the value changed.
Lit React wrapper is inconsistent with React behavior.

https://github.com/lit/lit/blob/fbda6d7b42b8acd19b388e9de0be3521da6b58bb/packages/react/src/create-component.ts#L265

### Reproduction

Open console. See that wrapper triggers setter on each re-render. Without wrapper, setter only triggered if value changed:

https://lit.dev/playground/#sample=examples%2Freact-basics&project=W3sibmFtZSI6ImFwcC50c3giLCJjb250ZW50IjoiaW1wb3J0IFJlYWN0IGZyb20gJ2h0dHBzOi8vZXNtLnNoL3JlYWN0QDE4JztcbmltcG9ydCB7Y3JlYXRlUm9vdH0gZnJvbSAnaHR0cHM6Ly9lc20uc2gvcmVhY3QtZG9tQDE4L2NsaWVudCc7XG5pbXBvcnQge2NyZWF0ZUNvbXBvbmVudH0gZnJvbSAnQGxpdC9yZWFjdCc7XG5pbXBvcnQge0RlbW9HcmVldGluZyBhcyBEZW1vR3JlZXRpbmdXQ30gZnJvbSAnLi9kZW1vLWdyZWV0aW5nLmpzJztcblxuLy8gQ3JlYXRlcyBhIFJlYWN0IGNvbXBvbmVudCBmcm9tIGEgTGl0IGNvbXBvbmVudFxuY29uc3QgRGVtb0dyZWV0aW5nID0gY3JlYXRlQ29tcG9uZW50KHtcbiAgcmVhY3Q6IFJlYWN0LFxuICB0YWdOYW1lOiAnZGVtby1ncmVldGluZycsXG4gIGVsZW1lbnRDbGFzczogRGVtb0dyZWV0aW5nV0MsXG59KTtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2FwcCcpISk7XG5cbnJvb3QucmVuZGVyKDxSb290IC8-KTtcbmZ1bmN0aW9uIFJvb3QoKSB7XG4gIGNvbnN0IFtzdGF0ZSwgc2V0U3RhdGVdID0gUmVhY3QudXNlU3RhdGUoZmFsc2UpO1xuICBSZWFjdC51c2VFZmZlY3QoKCkgPT4ge3NldEludGVydmFsKCgpID0-IHNldFN0YXRlKCh2YWx1ZSkgPT4gIXZhbHVlKSwgMTAwMCl9LCBbXSk7XG5cbiAgY29uc29sZS5sb2coXCJyZS1yZW5kZXJcIik7XG4gIHJldHVybiA8PlxuICAgPERlbW9HcmVldGluZyBuYW1lPVwiV3JhcHBlclwiIC8-XG4gICA8ZGVtby1ncmVldGluZyBuYW1lPVwiTm8gV3JhcHBlclwiIC8-XG4gIDwvPjtcbn0ifSx7Im5hbWUiOiJkZW1vLWdyZWV0aW5nLnRzIiwiY29udGVudCI6ImltcG9ydCB7aHRtbCwgY3NzLCBMaXRFbGVtZW50fSBmcm9tICdsaXQnO1xuaW1wb3J0IHtjdXN0b21FbGVtZW50LCBwcm9wZXJ0eX0gZnJvbSAnbGl0L2RlY29yYXRvcnMuanMnO1xuQGN1c3RvbUVsZW1lbnQoJ2RlbW8tZ3JlZXRpbmcnKVxuZXhwb3J0IGNsYXNzIERlbW9HcmVldGluZyBleHRlbmRzIExpdEVsZW1lbnQge1xuICBfbmFtZSA9IGBgO1xuICBAcHJvcGVydHkoKVxuICBzZXQgbmFtZShuYW1lOnN0cmluZykge1xuICAgIGNvbnNvbGUubG9nKFwic2V0dGVyIHRyaWdnZXJlZCwgb2xkIHZhbHVlOiBcIix0aGlzLl9uYW1lLCBcIi4gbmV3IHZhbHVlOiBcIiwgbmFtZSk7XG4gICAgdGhpcy5fbmFtZSA9IG5hbWU7XG4gIH1cbiAgZ2V0IG5hbWUoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5fbmFtZTtcbiAgfVxuXG4gIHJlbmRlcigpIHsgcmV0dXJuIHRoaXMubmFtZTsgfVxufVxuXG5kZWNsYXJlIGdsb2JhbCB7XG4gIGludGVyZmFjZSBIVE1MRWxlbWVudFRhZ05hbWVNYXAge1xuICAgICdkZW1vLWdyZWV0aW5nJzogRGVtb0dyZWV0aW5nO1xuICB9XG59XG4ifSx7Im5hbWUiOiJyZWFjdC5kLnRzIiwiY29udGVudCI6ImRlY2xhcmUgbW9kdWxlICdodHRwczovL2VzbS5zaC9yZWFjdEAxOCcge1xuICBleHBvcnQgZGVmYXVsdCBSZWFjdDtcbn1cblxuZGVjbGFyZSBtb2R1bGUgJ2h0dHBzOi8vZXNtLnNoL3JlYWN0LWRvbUAxOC9jbGllbnQnIHtcbiAgZXhwb3J0ICogZnJvbSAncmVhY3QtZG9tL2NsaWVudCc7XG59IiwiaGlkZGVuIjp0cnVlfSx7Im5hbWUiOiJpbmRleC5odG1sIiwiY29udGVudCI6Ik9wZW4gY29uc29sZS4gU2VlIHRoYXQgd3JhcHBlciB0cmlnZ2VycyBzZXR0ZXIgb24gZWFjaCByZS1yZW5kZXIuIFdpdGhvdXQgd3JhcHBlciwgc2V0dGVyIG9ubHkgdHJpZ2dlcmVkIGlmIHZhbHVlIGNoYW5nZWQuPGJyLz5cbjxzY3JpcHQgdHlwZT1cIm1vZHVsZVwiIHNyYz1cIi4vYXBwLmpzXCI-PC9zY3JpcHQ-PGRpdiBpZD1cImFwcFwiPjwvZGl2PlxuIn0seyJuYW1lIjoicGFja2FnZS5qc29uIiwiY29udGVudCI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwibGl0XCI6IFwiXjNcIixcbiAgICBcIkB0eXBlcy9yZWFjdFwiOiBcIjE4LjIuN1wiLFxuICAgIFwiQHR5cGVzL3JlYWN0LWRvbVwiOiBcIl4xOFwiLFxuICAgIFwiQHR5cGVzL3JlYWN0LWRvbS9jbGllbnRcIjogXCJeMThcIlxuICB9XG59IiwiaGlkZGVuIjp0cnVlfV0

### Workaround

1. React component needs to set properties manually in a use effect (bad ux for consumer)
2. Update to react 19 and don't use the wrapper. However, some enterprise and LTS customers will be stuck on react 18 for the next 2-3 years.
3. The web components needs to diff the value inside the setter - requires a lot of boilerplate:
- Is it Lit's recommendation that every component setter must always diff manually against the old value?
- This seems redundant with the `hasChanged()` concept. And it is not trivial for the setter to get a reference to its hasChanged() function.
- **Even besides that**, the `hasChanged()` concept is not sufficient as it compares against the getter value, rather than against the value that was set by the user. Imagine the component auto-casts a string to an object value in the setter.
- Ideally the setter should not trigger again if it is triggered again with the same string value - doing so is not trivial as each setter now needs two backing properties (the backing value and the raw user-set value). That is why LIt's wrapper behavior is problematic.

Issues with current wrapper behavior: needless work on re-render. causes infinite re-loading in some components. requires a lot of boilerplate to work around in web component.

### Is this a regression?

No or unsure. This never worked, or I haven't tried before.

### Affected versions

1.0.7

### Browser/OS/Node environment

Chrome: 137.0.7151.69 (Official Build) (arm64)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/react/src/create-component.ts around the linked line and reproduce the behavior with the provided Lit playground example. Compare wrapper updates with React 18's unchanged-value behavior; done means an unchanged prop no longer triggers the web component setter on re-render without requiring consumer workarounds.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.