Prevent initial callbacks for pattern-matching callbacks

When having a pattern-matching callback with two different types as input and output like

@app.callback(
Output({“type”: “input”, “index”: MATCH}, “value”),
Input({“type”: “button”, “index”: MATCH}, “n_clicks”),
)
def addButton …

the callback fires for every index, every time an id of type button is added to the layout. This leads to issues, because the button 1 callback fires not only if button 1 is clicked, but also if e.g. button 5 is added to the layout.

As a workaround I have added a dcc.Store called noUpdate to distinguish between user clicks and initial calls because a button was added. I check callback_context.triggered_prop_ids if the store is present and if it is, PreventUpdate is raised.

from dash import (MATCH,Dash,Input,Output,Patch,callback_context,html,dcc,no_update)from dash.exceptions import PreventUpdate

app = Dash()

app.layout = html.Div(
    [
        dcc.Store(id="noUpdate", data=True),
        html.Button("Add", id="buttonAdd"),
        html.Div(id="container1", children=[]),
        html.Div(id="container2", children=[]),
    ]
)


@app.callback(
    Output("container1", "children"),
    Output("container2", "children"),
    Output("noUpdate", "data"),
    Input("buttonAdd", "n_clicks"),
    prevent_initial_call=True,
)
def addButton(nClicks):
    container1 = Patch()
    container1.append(
        dcc.Input(f"Input {nClicks}", id={"type": "input", "index": nClicks})
    )
    container2 = Patch()
    container2.append(
        html.Button(
            f"Button {nClicks}", id={"type": "button", "index": nClicks}, n_clicks=3
        )
    )
    return container1, container2, True


@app.callback(
    Output({"type": "input", "index": MATCH}, "value"),
    Input({"type": "button", "index": MATCH}, "n_clicks"),
    Input("noUpdate", "data"),
    prevent_initial_call=True,
)
def test(nClicks, _):
    # Prevent this callback from running if it was triggered by the add button callback.
    # Remove the following 2 lines to see the behavior without extra dcc.Store.
    if "noUpdate" in callback_context.triggered_prop_ids.values():
        raise PreventUpdate
    print(callback_context.triggered)

    return no_update


if __name__ == "__main__":
    app.run(debug=True)

While this approach works great in the small example, it leads to many unnecessary callback evocation if the noUpdate store is an input to multiple callbacks. Callbacks now fire solely because they have noUpdate as Input and not because any of there real inputs have changed. Does anyone have a better suggestions how to ignore the callbacks created when adding a button? Is there some way to prevent subsequent callbacks or mark the source of a callback trigger?

This might be helpful:

This solution will still trigger the callback, but cancel it early one through the PreventUpdate.

I would also like a feature that prevent pattern matching callbacks from firing should because a components enters or leaves the layout.

That is correct. As far as I know, what you ask is currently not possible. You might create an issue on Github.

Out of curiosity, whats the issue if the callbacks get triggered because a component gets added to the layout?

Thank you for the link you shared. I guess it doesn’t make much of a difference if one handles a single ALL callback or a bunch of MATCH callbacks. The core issue, triggering when the layout changes persists.

For now there is no other issue other than a bunch of unnecessary callbacks. I wanted to use the same preventUpdate strategy to break circular callback loops. The issue there is, if I use only one dcc.Store every callback is fired with every user interaction which relies on preventUpdate. In short, for every user interaction 50+ callbacks run and immediately exit. It just seems like bad design to me, but I don’t know how to avoid it.