material-components / material-components/material-web

Switch and Checkbox is not updated when reverted in change event

Open
#5,147 2 comments 1 reaction 0 assignees View on GitHub

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

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 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.