plotly / dash

Data Apps & Dashboards for Python. No JavaScript Required.
https://plotly.com/dash
MIT License
21.42k stars 2.06k forks source link

Using relayoutData as input causes callback to be triggered on initial load even if prevent_initial_call=True #2668

Open MisterSweety opened 1 year ago

MisterSweety commented 1 year ago

Bug description Hello there, I am working on a dash app in which user changes to the relayoutData of a visual e.g. changing the zoom level should trigger a callback function and I want this callback to not be run on initial_load. However, it seems that using relayoutData as an input for my callback function results in the callback being triggered on initial load regardless of whether I set prevent_initial_call=True or False. I have implemented a fix for this issue myself by using the following conditional check at the start of the callback function:

if not ctx.triggered_id:
       raise PreventUpdate

Here is the callback definition:

@dash_app.app.callback(
    Output("fig_drydown", "figure", allow_duplicate=True),
    Output("fig_shot_size", "figure", allow_duplicate=True),
    Output("fig_drain_percentage", "figure", allow_duplicate=True),
    Output("fig_ec_info", "figure", allow_duplicate=True),
    Output("fig_irrigation_strategy", "figure", allow_duplicate=True),
    Input("fig_drydown", "relayoutData"),
    Input("fig_shot_size", "relayoutData"),
    Input("fig_drain_percentage", "relayoutData"),
    Input("fig_ec_info", "relayoutData"),
    Input("fig_irrigation_strategy", "relayoutData"),
    State("fig_drydown", "figure"),
    State("fig_shot_size", "figure"),
    State("fig_drain_percentage", "figure"),
    State("fig_ec_info", "figure"),
    State("fig_irrigation_strategy", "figure"),
    State("cultivation_x_axis_range", "data"),
    prevent_initial_call=True,
)
def adjust_visual_zoom(
    fig_drydown_relayout: go.Figure,
    fig_shot_size_relayout: go.Figure,
    fig_drain_percentage_relayout: go.Figure,
    fig_ec_info_relayout: go.Figure,
    fig_irrigation_strategy_relayout: go.Figure,
    fig_drydown: go.Figure,
    fig_shot_size: go.Figure,
    fig_drain_percentage: go.Figure,
    fig_ec_info: go.Figure,
    fig_irrigation_strategy: go.Figure,
    initial_x_axis_range: dict,
):

Context Please provide us your environment, so we can easily reproduce the issue.

alexcjohnson commented 1 year ago

Thanks @EasyEesteren - I think what's going on here is that during initial draw plotly.js is pushing some information back to figure.layout (probably due to autorange or autosize) which triggers a relayout event, and we don't know that this counts as part of the initialization sequence so prevent_initial_call doesn't exclude it... but it should! There would be something similar going on in #2671 if we adjusted those props upon validating them.

MisterSweety commented 1 year ago

@alexcjohnson Thanks for this explanation and your prompt reply! As I mentioned we implemented a fix on our end so we can work around this but I still wanted to flag this issue to your attention. Please let me know if you need anymore context or information