Skip to content

[BUG] Setting children to None leaves removed components registered: their callbacks keep firing and the subtree can never be mounted again #4033

Description

@vdidon

Summary: if a callback removes a subtree by returning children=None, the removed components stay registered: callbacks targeting them keep firing, and the subtree can never be rendered again. Returning [] works correctly.

Describe your context

dash                 4.4.1  (same on 4.5.0rc0, 4.0.0, 3.3.0, 2.18.2)
plotly               6.6.0
  • Python 3.10, Windows 11, Microsoft Edge 154 (Chromium)
  • Same with debug=True and debug=False, no error in the console or the dev tools

Describe the bug

from dash import Dash, Input, Output, ctx, dcc, html

app = Dash(__name__, suppress_callback_exceptions=True)

app.layout = html.Div([
    dcc.Interval(id="tick", interval=1000),
    html.Button("Mount", id="mount"),
    html.Button("Unmount", id="unmount"),
    html.Div(id="container"),
])


@app.callback(
    Output("container", "children"),
    Input("mount", "n_clicks"),
    Input("unmount", "n_clicks"),
    prevent_initial_call=True,
)
def toggle(_mount, _unmount):
    if ctx.triggered_id == "mount":
        return html.Div(id="view", children=html.Div(id="view-tick"))
    return None  # returning [] instead works as expected


@app.callback(Output("view-tick", "children"), Input("tick", "n_intervals"))
def update_view(n):
    print(f"update_view called, n_intervals={n}", flush=True)
    return f"tick {n}"


if __name__ == "__main__":
    app.run(debug=False)
  1. Click Mount: tick N is displayed, update_view runs every second. Correct.
  2. Click Unmount: the text disappears, but the browser keeps sending update_view requests every second (visible in the server log).
  3. Click Mount again: the toggle request returns the subtree (HTTP 200), but #view never appears in the DOM, until the page is reloaded.

With return [] in toggle, step 2 sends no request and step 3 displays the view again.

Expected behavior

None and [] should both remove the subtree: no more callbacks for the removed components, and the subtree can be mounted again.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions