material-components / material-components/material-web
Switch and Checkbox is not updated when reverted in change event
Nobody has claimed this yet.
- Dominant language
- SCSS
- Stars
- 11.3k
- Forks
- 1.1k
- Avg merge
- 20h 28m
- Merged PRs (30d)
- 13
Description
### What is affected?
Component
### Description
In some scenarios when a switch/checkbox is reverted back under certain conditions in `change` event handler, the underlying checkbox is not updated correctly and on the next click, the switch/checkbox does not get to the new value that it should be.
```js
s.addEventListener("change", () => {
console.log(s.selected);
if (true) { s.selected = false; } // In real scenarios the condition is met sometimes.
});
```
Here's a simulation of what my app behavior is like: [Lit Playground](https://lit.dev/playground/#project=W3sibmFtZSI6InNpbXBsZS1ncmVldGluZy50cyIsImNvbnRlbnQiOiJpbXBvcnQgXCJAbWF0ZXJpYWwvd2ViL2FsbFwiOyJ9LHsibmFtZSI6ImluZGV4Lmh0bWwiLCJjb250ZW50IjoiPCFET0NUWVBFIGh0bWw-XG48aGVhZD5cbiAgPHNjcmlwdCB0eXBlPVwibW9kdWxlXCIgc3JjPVwiLi9zaW1wbGUtZ3JlZXRpbmcuanNcIj48L3NjcmlwdD5cbjwvaGVhZD5cbjxib2R5PlxuICAgIDxwPlxuICAgICAgICA8bWQtc3dpdGNoPjwvbWQtc3dpdGNoPiAgXG4gICAgICBcbiAgICAgICAgPHNwYW4-PC9zcGFuPlxuICAgICAgPC9wPlxuICAgICAgXG4gIDxzY3JpcHQ-XG4gICAgbGV0IHNob3VsZFJldmVydCA9IGZhbHNlO1xuICAgIGNvbnN0IHNwYW4gPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFwic3BhblwiKTtcbiAgICBcbiAgICBjb25zdCBzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcIm1kLXN3aXRjaFwiKTtcbiAgICBzLmFkZEV2ZW50TGlzdGVuZXIoXCJjaGFuZ2VcIiwgKCkgPT4ge1xuICAgICAgICBjb25zb2xlLmxvZyhzLnNlbGVjdGVkKTtcbiAgICAgICAgc3Bhbi50ZXh0Q29udGVudCA9IFwiXCI7XG4gICAgICAgIFxuICAgICAgICBpZiAocy5zZWxlY3RlZCkge1xuICAgICAgICAgICAgc2hvdWxkUmV2ZXJ0ID0gIXNob3VsZFJldmVydDtcblxuICAgICAgICAgICAgaWYgKHNob3VsZFJldmVydCkge1xuICAgICAgICAgICAgICBzcGFuLnRleHRDb250ZW50ID0gXCJPaCBubywgeW91IGNhbiBub3QgY2hlY2sgaXQgbm93XCI7XG4gICAgICAgICAgICAgIHMuc2VsZWN0ZWQgPSBmYWxzZTsgIFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSk7XG4gIDwvc2NyaXB0PlxuPC9ib2R5PlxuIn0seyJuYW1lIjoicGFja2FnZS5qc29uIiwiY29udGVudCI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwibGl0XCI6IFwiXjMuMC4wXCIsXG4gICAgXCJAbGl0L3JlYWN0aXZlLWVsZW1lbnRcIjogXCJeMi4wLjBcIixcbiAgICBcImxpdC1lbGVtZW50XCI6IFwiXjQuMC4wXCIsXG4gICAgXCJsaXQtaHRtbFwiOiBcIl4zLjAuMFwiXG4gIH1cbn0iLCJoaWRkZW4iOnRydWV9XQ). If user checks the switch but then some condition is not satisfied, I revert it back. Then the next time user clicks it, nothing happen and user has to click it a second time.
### Reproduction
[Lit Playground](https://lit.dev/playground/#project=W3sibmFtZSI6InNpbXBsZS1ncmVldGluZy50cyIsImNvbnRlbnQiOiJpbXBvcnQgXCJAbWF0ZXJpYWwvd2ViL2FsbFwiOyJ9LHsibmFtZSI6ImluZGV4Lmh0bWwiLCJjb250ZW50IjoiPCFET0NUWVBFIGh0bWw-XG48aGVhZD5cbiAgPHNjcmlwdCB0eXBlPVwibW9kdWxlXCIgc3JjPVwiLi9zaW1wbGUtZ3JlZXRpbmcuanNcIj48L3NjcmlwdD5cbjwvaGVhZD5cbjxib2R5PlxuICAgIDxwPlxuICAgICAgICA8bWQtc3dpdGNoPjwvbWQtc3dpdGNoPiAgXG4gICAgICA8L3A-XG4gICAgICBcbiAgICAgIDxwPlxuICAgICAgICA8bWQtY2hlY2tib3g-PC9tZC1jaGVja2JveD4gIFxuICAgICAgPC9wPlxuICAgICAgXG4gICAgICA8cD5cbiAgICAgICAgPGlucHV0IHR5cGU9XCJjaGVja2JveFwiIC8-XG4gICAgICA8L3A-XG4gIFxuICA8c2NyaXB0PlxuICAgIGNvbnN0IHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFwibWQtc3dpdGNoXCIpO1xuICAgIHMuYWRkRXZlbnRMaXN0ZW5lcihcImNoYW5nZVwiLCAoKSA9PiB7XG4gICAgICAgIGNvbnNvbGUubG9nKHMuc2VsZWN0ZWQpO1xuICAgICAgICBzLnNlbGVjdGVkID0gZmFsc2U7XG4gICAgfSk7XG5cbiAgICBjb25zdCBjaGsgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFwibWQtY2hlY2tib3hcIik7XG4gICAgY2hrLmFkZEV2ZW50TGlzdGVuZXIoXCJjaGFuZ2VcIiwgZSA9PiB7XG4gICAgICAgIGNvbnNvbGUubG9nKGNoay5jaGVja2VkKTtcbiAgICAgICAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKCgpID0-IGNoay5jaGVja2VkID0gZmFsc2UpO1xuICAgICAgICAvL2Noay5jaGVja2VkID0gZmFsc2U7XG4gICAgfSk7XG5cbiAgICBjb25zdCBjaGsyID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcImlucHV0XCIpO1xuICAgIGNoazIuYWRkRXZlbnRMaXN0ZW5lcihcImNoYW5nZVwiLCAoKSA9PiB7XG4gICAgICAgIGNvbnNvbGUubG9nKGNoazIuY2hlY2tlZCk7XG4gICAgICAgIGNoazIuY2hlY2tlZCA9IGZhbHNlO1xuICAgIH0pO1xuICA8L3NjcmlwdD5cbjwvYm9keT5cbiJ9LHsibmFtZSI6InBhY2thZ2UuanNvbiIsImNvbnRlbnQiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcImxpdFwiOiBcIl4zLjAuMFwiLFxuICAgIFwiQGxpdC9yZWFjdGl2ZS1lbGVtZW50XCI6IFwiXjIuMC4wXCIsXG4gICAgXCJsaXQtZWxlbWVudFwiOiBcIl40LjAuMFwiLFxuICAgIFwibGl0LWh0bWxcIjogXCJeMy4wLjBcIlxuICB9XG59IiwiaGlkZGVuIjp0cnVlfV0)
- The switch has the problem: open the console then clicks on it. The console prints out `true`, `false`, `true`, `false`, ... . The correct behavior should print `true` every time.
- The checkbox use a workaround with `requestAnimationFrame`.
- The standard `` does not have this issue and correctly prints out `true` every time.
### Workaround
Change the state in the next frame using `requestAnimationFrame` or `setTimeout`.
---
I spent a few hours investigating this issue and strangely enough this probably isn't Material issue but maybe Lit's. See [this SO question](https://stackoverflow.com/q/77406360/653457) I made. At first I thought the issue was [`?checked` instead of `.checked`](https://stackoverflow.com/a/55994717/653457) but even changing that, the issue still happens. As the above question stated, when `render()` is called, the `selected` property already has a correct value so it's not an event handling issue as well.
When tweakling with the source code, I found two solutions:
1. Use `live()` as the answer suggested. I am not sure what performance impact this may have.
2. In the switch's `handleChange()`, simply use `this.selected = !this.selected` instead of getting it from the underlying checkbox. This however leave the underlying checkbox state not "up-to-date" with the component.
I am happy to provide a PR for this fix if approved.
### Is this a regression?
No or unsure. This never worked, or I haven't tried before.
### Affected versions
1.0.1
### Browser/OS/Node environment
Microsoft Edge
Version 118.0.2088.76 (Official build) (64-bit)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the switch and checkbox handleChange() and render() paths, then reproduce the behavior using the linked Lit Playground. Compare the component behavior with the standard checkbox and the requestAnimationFrame workaround; done means reverting in a change handler keeps the underlying state synchronized so repeated clicks consistently report the new value.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- frontend, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100