"raise PreventUpdate" prevents any side channel property updates through "set_props"
Thanks for maintaining this library, I really appreciate it!
Describe your context
dash 2.17.0
dash-core-components 2.0.0
dash-extensions 1.0.15
dash-html-components 2.0.0
dash-table 5.0.0
Describe the bug
When set_props is used within a callback that later hits raise PreventUpdate, the properties set by set_props do not propagate back to the server when the callback ends. This is in direct contrast to no_update. Based on my understanding of the docs here, both raise PreventUpdate and return no_update (if applied to all outputs) should both have the same behavior in regards to updating outputs.
@callback(...)
def callback_prevent_update(...):
"""Does NOT set prop_id_here->is_open to True"""
set_props("prop_id_here", {"is_open": True})
raise PreventUpdate
@callback(...)
def callback_no_update(...):
"""Does set prop_id_here->is_open to True"""
set_props("prop_id_here", {"is_open": True})
return no_update
Expected behavior
I expected that raise PreventUpdate would have the exact same behavior as return no_update (when applied to all outputs) which is that the outputs of a callback do not update, but the set_props call still goes through.
If it's expected that these produce two different behaviors, then it might be worth updating the caveats here with this case as it's unexpected behavior compared to the standard Input/Output behavior in callbacks. I'm happy to help update the documentation if there's a code pointer on where to add it! ( I didn't see the docs folder on a quick initial look)
Screenshots
In the chrome networking tab, the original callback in the example returns the following:
When raise PreventUpdate is added, the example returns the following (Note the status code in the second image is now 204 instead of 200):
Minimal Reproducible Example
This is taken directly from here, with the only change being the raise PreventUpdate line
Steps to reproduce
- Load the page
- Click on one of the rows
- A modal should have popped up, but does not due to the
raise PreventUpdate
import dash_ag_grid as dag
from dash import Dash, Input, html, ctx, callback, set_props
from dash.exceptions import PreventUpdate
import dash_bootstrap_components as dbc
app = Dash(__name__, external_stylesheets=[dbc.themes.SPACELAB])
rowData = [
{"make": "Toyota", "model": "Celica", "price": 35000},
{"make": "Ford", "model": "Mondeo", "price": 32000},
{"make": "Porsche", "model": "Boxster", "price": 72000},
]
app.layout = html.Div(
[
dag.AgGrid(
id="setprops-row-selection-popup",
rowData=rowData,
columnDefs=[{"field": i} for i in ["make", "model", "price"]],
columnSize="sizeToFit",
dashGridOptions={"rowSelection": "single", "animateRows": False},
),
dbc.Modal(
[
dbc.ModalHeader("More information about selected row"),
dbc.ModalBody(id="setprops-row-selection-modal-content"),
dbc.ModalFooter(dbc.Button("Close", id="setprops-row-selection-modal-close", className="ml-auto")),
],
id="setprops-row-selection-modal",
),
]
)
@callback(
Input("setprops-row-selection-popup", "selectedRows"),
Input("setprops-row-selection-modal-close", "n_clicks"),
prevent_initial_call=True,
)
def open_modal(selection, _):
if ctx.triggered_id == "setprops-row-selection-modal-close":
# Close the modal
set_props("setprops-row-selection-modal", {'is_open': False})
elif ctx.triggered_id == "setprops-row-selection-popup" and selection:
# Open the modal and display the selected row content
content_to_display = "You selected " + ", ".join(
[
f"{s['make']} (model {s['model']} and price {s['price']})"
for s in selection
]
)
set_props("setprops-row-selection-modal", {'is_open': True})
set_props("setprops-row-selection-modal-content", {'children': content_to_display})
raise PreventUpdate
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8050, debug=True)