marimo-team / marimo

A reactive notebook for Python — run reproducible experiments, execute as a script, deploy as an app, and version with git.
https://marimo.io
Apache License 2.0
7.97k stars 280 forks source link

[anywidget] frontend state "reset" after the first interaction #2609

Open gereleth opened 1 month ago

gereleth commented 1 month ago

Describe the bug

This is anywidget's counter widget example with some console.logs sprinkled in. If we run this in a cell in marimo 0.9.7 and click the counter button then the value briefly changes to 61 and then immediately reverts back to 60. So the button shows "count is 60" but curiously if we check c.value in another cell then it shows 61 - a desync between python and frontend.

import anywidget
import traitlets

class CounterWidget(anywidget.AnyWidget):
    _esm = """
    function render({ model, el }) {
      let count = () => model.get("value");
      let btn = document.createElement("button");
      btn.classList.add("counter-button");
      btn.innerHTML = `count is ${count()}`;
      btn.addEventListener("click", () => {
        console.log(`counter button click - set value to ${count()+1}`)
        model.set("value", count() + 1);
        model.save_changes();
      });
      model.on("change:value", () => {
        console.log(`value change event - count is ${count()}`)
        btn.innerHTML = `count is ${count()}`;
      });
      el.appendChild(btn);
    }
    export default { render };
    """
    _css = """
    .counter-button {
      background-image: linear-gradient(to right, #a1c4fd, #c2e9fb);
      border: 0;
      border-radius: 10px;
      padding: 10px 50px;
    }
    """
    value = traitlets.Int(0).tag(sync=True)

c = CounterWidget()
c.value = 60
c

After the first button click the js console shows the following messages:

counter button click - set value to 61
value change event - count is 61
value change event - count is 60

That third line shouldn't be there but some sort of "reset" happens in the frontend after the first button click. Subsequent clicks seem to work fine.

It looks like the "reset" is related to the assignment c.value = 60. If we comment out the assignment then the bug doesn't happen. If we put in multiple assignments - then we see them all get replayed after the first click. This replay of state changes definitely shouldn't happen.

For context: my anywidget-based jupyter_bbox_widget still doesn't work in marimo after the fixes in #2506 related to binary data. And there aren't even any error messages now =). It sort of looks like a "reset" might be happening there as well because the image shows up initially but then disappears as soon as I do anything.

Environment

{
  "marimo": "0.9.7",
  "OS": "Linux",
  "OS Version": "6.0.12-100.fc35.x86_64",
  "Processor": "x86_64",
  "Python Version": "3.10.8",
  "Binaries": {
    "Browser": "--",
    "Node": "v22.3.0"
  },
  "Dependencies": {
    "click": "8.1.7",
    "importlib-resources": "missing",
    "jedi": "0.19.1",
    "markdown": "3.7",
    "pygments": "2.18.0",
    "pymdown-extensions": "10.11.2",
    "ruff": "0.6.9",
    "starlette": "0.39.2",
    "tomlkit": "0.13.2",
    "typing-extensions": "4.12.2",
    "uvicorn": "0.31.0",
    "websockets": "12.0"
  },
  "Optional Dependencies": {
    "anywidget": "0.9.13"
  }
}

Code to reproduce

(Run the counter widget snippet above in a marimo cell and click the counter button once. Note how the count is still displayed as 60 when it should be 61)

mscolnick commented 1 month ago

hi @gereleth, apologies you keep experiencing issues with rendering your anywidget content.

I made a change that fixes the issue you raised: https://github.com/marimo-team/marimo/pull/2620 I'm not sure if it is the right change or if there are side-effects. I'm also not sure if this change will get your library to work - I tried to run your library, but still had issues - it could be just me not able to run it, but you may know better.

gereleth commented 1 month ago

Thanks, @mscolnick, that change fixed the counter example but not the bbox widget. I don't know what's going on with the disappearing image there. I guess I will wait till anywidget support in marimo matures a little.