Hello everyone,
I have been working with dash < 3.0.0 and when upgrading encountered an unexpected behavior in a callback that can be triggered either by an interval or a button.
Before, if the callback was triggered by an interval and it returns a no_update
to a DataTable
property, it wouldn’t update the component. Dash wouldn’t re-render the element and therefore any edits/changes on the client side wouldn’t be lost even if the callback was triggered.
Now, with dash > 3.0.0, even when I pass a no_update
in the callback, it re-renders the complete component, which makes any edit to be lost.
Please see the code below. With, dash > 3.0.0 you have 1 second interval to edit 'table-B'
before the interval triggers the callback and the changes are lost.
Not really sure if this is the real purpose of no_update
but thought it was worthwhile to ask the community.
PS. Actually, for DataTable
in versions dash <3.0.0 I had to pass a no_update
to component_property
'columns'
and 'data'
. If it is passed only to 'data'
dash would re-render the whole DataTable
import numpy as np
import dash
from dash import dcc, Dash, Input, Output, callback, ctx
from dash.html import Br, Div, Button
from dash.dash_table import DataTable
app = Dash(__name__,)
data_A = [{"Column1": 0, "Column2": 0}]
data_B = [{'Column 1': 'B1', 'Column 2': 'B2'},
{'Column 1': 'B3', 'Column 2': 'B4'}]
app.layout = Div([
Div(children=[
DataTable(id='table-A', columns=[{'name': 'Column1', 'id': 'Column1'},
{'name': 'Column2', 'id': 'Column2'}],
data=data_A),
]),
Br(),
DataTable(id='table-B', columns=[{'name': 'Column 1', 'id': 'Column 1'},
{'name': 'Column 2', 'id': 'Column 2'}],
editable=True,
data=data_B),
Br(),
Button('Update Both Tables', id='update-button'),
dcc.Interval(id='interval-component', interval=1000, n_intervals=0),
])
@callback(
Output(component_id='table-B', component_property='columns'),
Output(component_id='table-B', component_property='data'),
Output(component_id='table-A', component_property='data'),
Input(component_id='interval-component', component_property='n_intervals'),
Input(component_id='update-button', component_property='n_clicks'),
prevent_initial_call=True
)
def update_tables(n_intervals, n_clicks):
button_id = ctx.triggered_id
if button_id == 'update-button':
data_A = [{"Column1": 0, "Column2": 0}]
num = np.random.rand() * 10
data_B = [{'Column 1': num, 'Column 2': num},
{'Column 1': num, 'Column 2': num}]
return dash.no_update, data_B, data_A
else:
num = np.random.rand() * 10
data_A = [{"Column1": num, "Column2": num * 10}]
return dash.no_update, dash.no_update, data_A
if __name__ == "__main__":
app.run(
debug=True,
port=8050,
host='0.0.0.0',
)