Button n_clicks resets after click / callback is triggered multiple times within a few seconds

In my Dash Application, I am using a dbc.Button - dbc.Button("Show P", id="show-p-btn", className="me-1", n_clicks=0),

In my callback, I have a condition that checks for the value of n_clicks of the button.

import dash
from dash.dependencies import Input, Output, State
import dash_bootstrap_components as dbc
import datetime

layout = html.Div([

                                  dbc.Input(id="output-1"),
 
                                  dbc.Button("Show P", id="show-p-btn", className="me-1", n_clicks=0),
 
                             ])


@application.callback(Output("output-1","value"),
                      [
                        Input("show-p-btn","n_clicks")
                      ]
                     )
def a1(n_clicks_val):

    now = datetime.now()
    current_time = now.strftime("%H:%M:%S")

    ctx = dash.callback_context
    print(ctx.triggered, current_time)

    if n_clicks_val > 0:

        x = 1

    else:

       y = 0
   
    if n_clicks_val > 0:
        return (x)
   else:
        return(y)

I started noticing some strange behavior, where the button resets after I click it once and the value is set to 0. I print the output of ctx.triggered

[{'prop_id': 'show-p-btn.n_clicks', 'value': 1}] — This is expected.

[{'prop_id': 'show-p-btn.n_clicks', 'value': None}] — This is Not expected.

This is happening every time and I only click the button once. My condition doesn’t work because of this reset / unexpected behavior.

I have several components in the app layout, but I only see show properties being triggered and reset once I click it.

I am on :

dash 2.6.0
dash-bootstrap-components 0.13.0

@AnnMarieW any thoughts on this?

Do you have some more code? Or even better the full code or at least a minimum working example? It’s hard to figure out the problem with only the code snippet above.

@keval Do you happen to have more than one browser window open with that app running?

@AnnMarieW No, I am running this only in 1 browser window.

Well, as @yanboe mentioned, it’s hard to help without a minimal example that reproduces the error. For more info see: How to Get your Questions Answered on the Plotly Forum

@yanboe @AnnMarieW

I have added more details to the question and a minimal example to see what’s going on.

Basically, when I click the button, the n_click value is updated to 1, and returns x which is the expected behavior. However, within a few seconds it resets to either 0 or None. I verify this with timestamp and by printing the output of ctx.triggered.

Because of this behavior, I lose my updates and the component is not rendered correctly.

Is it expected behavior to reset n_clicks property so quickly?

@yanboe I have added a working example. Let me know if you have any other questions.

@keval

I can’t duplicate the problem, but at the beginning of your callback try adding:

    if n_clicks_val is None:
        return dash.no_update
1 Like

@AnnMarieW

For some reason, the button is triggered 2x when clicked. I see it increment to 1 and then reset back to 0. This breaks my code.

Is it expected behavior? Is it suppose to reset right away? It use to work - but something seems to have changed in the library?

I can’t really reproduce your issue. I’ve modified your code a bit to make it run on my computer, can you try if this works for you?

from dash import Dash, Input, Output, html, ctx
import dash_bootstrap_components as dbc
from datetime import datetime

app = Dash(__name__)

app.layout = html.Div(
    [
        dbc.Input(id="output-1"),
        dbc.Button("Show P", id="show-p-btn", className="me-1", n_clicks=0),
    ]
)


@app.callback(
    Output("output-1", "value"),
    Input("show-p-btn", "n_clicks")
)
def a1(n_clicks_val):
    now = datetime.now()
    current_time = now.strftime("%H:%M:%S")

    print(ctx.triggered, current_time)

    if n_clicks_val > 0:
        return 1
    else:
        return 0


if __name__ == '__main__':
    app.run_server(debug=True)

And which version of Dash are you using?

My callback is fairly length and complex, so I tried to create a minimal example.

I am on Dash 2.6.0. The issue did occur after upgrading. Not sure if that’s a coincidence.

I am wondering if reseting the n_clicks property of the button is expected behavior? As we can see from the output of ctx.trigger and timestamps, the value is reset to 0 right after increment.

[{'prop_id': 'show-p-btn.n_clicks', 'value': 1}] Timestamp: 07:42:30 .

[{'prop_id': 'show-p-btn.n_clicks', 'value': None}] [{'prop_id': 'show-p-btn.n_clicks', 'value': 0}] Timestamp: 07:42:41

@yanboe @AnnMarieW

Even when I try to read the State of the show-p-btn, it is reset to 0.

My conditions are based on n_click and this behavior breaks the code. Is this a potential bug? Should we open an issue on github? Thanks.

Hi @keval Neither @yanboe or I could duplicate your issue.

Can you provide a complete example that we can run and see the error? If this is part of a multi-page app, it might be easier to make a GitHub repo, but please try to make it as minimal as possible - just enough to show the error.

@AnnMarieW @yanboe

The behavior persists for me. I have tried to clarity, and create a minimal reproducible example here:

@keval This is not a complete example. Please, make an example that someone can copy, paste, run, and it will show the error you describe with the callback is triggered multiple times.

More info on how to do that here: How to Get your Questions Answered on the Plotly Forum

@AnnMarieW

I am not able to reproduce the issue in a new environment either. Perhaps, it’s something in my environment or some dependency that could be causing the issue.

For some reason, the callback is triggered 2x times and the selections are reset which breaks my code.

In what possible scenarios would callback be triggered 2x within few seconds? I see my code working and then reset back to default in few seconds without any explicit user triggers or actions.

I can’t think of anything off hand… are you updating the url in a callback? That might cause some conflict if it’s not done correctly. Are you getting any error messages in the console?

No, nothing in the console that indicates issue with this. Here’s my index.py which is the main controller file.

from app import app
import dash
from dash.dependencies import Input, Output, State
from dash import dcc
import dash_bootstrap_components as dbc
from dash import html

app.layout = html.Div([

                            html.Div(

                                [
                                 
                                    dbc.Nav(
                                        [
                                            dbc.NavLink("Tab1", href="/tab1", active="partial"),
                                            dbc.NavLink("Tab2", href="/tab2", active="partial"),
                                           

                                        ],
                                        vertical=True,
                                        fill=True,
                                        pills=True,
                                    ),
                                ],

                                style = {
                                    "position": "fixed",
                                    "top": 0,
                                    "left": 0,
                                    "bottom": 0,
                                    "width": "10rem",
                                    "padding": "1rem 1rem",
                                    "background-color": "#f8f9fa",
                                },

                            ),

                            dcc.Location(id='url'),

                            html.Div(id='page-content'),

                           
                            ],

                        )

])

# Render page content
@app.callback(Output("page-content", "children"),
              [
                Input('url', 'pathname')
              ]
             )
def display_content(pathname):

    print(pathname)

    if pathname in ["/","/dashboard/","/dashboard2"]:
        return tab1.layout

    elif pathname == "/tab1":
        return tab1.layout

    elif pathname == "/tab2":
        return tab2.layout

    else:
        return dash.no_update