Dash应用动态更新块时如何保存按钮n_clicks会话状态?
解决Dash动态组件刷新后隐藏状态丢失且会话独立的问题
要解决这个问题,核心是用会话级状态存储保存每个用户的模块隐藏状态,避免页面刷新时重置按钮状态。Dash的dcc.Store组件正好满足需求——它会为每个用户会话独立存储数据,不会在用户间共享。
关键实现步骤
- 添加会话级状态存储:在布局中加入
dcc.Store,用字典结构保存每个模块的按钮点击次数 - 组件生成时读取存储状态:生成卡片组件时,从Store中读取对应模块的状态,设置按钮初始点击次数和卡片样式
- 同步状态到存储:修改隐藏/显示回调,将按钮点击状态同步到Store中
- 数据更新时复用存储状态:数据刷新回调读取Store状态,确保组件重建后保持之前的隐藏状态
修改后的完整代码
from dash import dcc, html, Input, Output, callback, MATCH, ctx, State from datetime import datetime import dash_bootstrap_components as dbc refreshing_data = [ [], [], [], [[1, '16/09 12:00', 'name', 'https://google.com/', 'status', '1 day 1 h', '1 day 1 h']], [[1, '16/09 12:00', 'name', 'https://google.com/', 'status', '1 day 1 h', '1 day 1 h']], [], [], [], [], [], [[1, '16/09 12:00', 'name', 'https://google.com/', 'status', '1 day 1 h', '1 day 1 h']], 'Last refresh at 15:19' ] switch_case = { 0: 'H0', 1: 'H1', 2: 'H2', 3: 'H3', 4: 'H4', 5: 'H5', 6: 'H6', 7: 'H7', 8: 'H8', 9: 'H9', 10: 'H10' } def template_small_card(row): date_req = row[1] operation = row[2] link = row[3] status = row[4] card = html.Div([ html.H6("Content is {}, {}, {}, {}".format(date_req, operation, link, status)) ]) return card def template_big_card(list_of_small_cards, big_name, hidden_states): # 读取当前模块的历史点击次数,默认0 module_index = big_name n_clicks = hidden_states.get(module_index, 0) # 根据点击次数设置初始显示状态 initial_style = {'display': 'none'} if n_clicks % 2 == 1 else {} card = html.Div( [ html.Div([ html.Div( [ html.Button(children=[html.Img(src='assets/vector1.png')], n_clicks=n_clicks, id={"type": "dynamic-button", "index": module_index}, ), ] ), html.Div([ html.H4("{}".format(big_name), className="card-title"), ]), ]), html.Hr(), html.Div( [*list_of_small_cards], id={"type": "dynamic-card", "index": module_index}, style=initial_style ) ] ) return dbc.Row(children=[card], className="g-0") def generate_big_card(data, big_name, hidden_states): if not data: return [] else: result = [template_small_card(ind) for ind in data] big_card = template_big_card(result, switch_case[big_name], hidden_states) return big_card layout = html.Div([ html.Div('Request queue'), html.H6(id='time_update_1_vert'), dcc.Interval( id='interval-component_vert', interval=30 * 1000, # 修正为30秒刷新间隔 n_intervals=0 ), # 会话级存储,保存每个模块的点击状态,关闭页面自动清空 dcc.Store(id='hidden-models-store', storage_type='memory', data={}), dbc.Row(id='row-tables_vert') ]) @callback( Output('time_update_1_vert', 'children'), Output('row-tables_vert', 'children'), Input('interval-component_vert', 'n_intervals'), State('hidden-models-store', 'data'), ) def update_data(n_intervals, hidden_states): list_of_df = refreshing_data columns_for_row = [] time_update = datetime.today().strftime('%Y-%m-%d %H:%M:%S') for i in range(0, 11): dbc_column = generate_big_card(list_of_df[i], i, hidden_states) if dbc_column: columns_for_row.append(dbc_column) return time_update, columns_for_row @callback( Output({"type": "dynamic-card", "index": MATCH}, "style"), Output('hidden-models-store', 'data'), Input({"type": "dynamic-button", "index": MATCH}, "n_clicks"), State('hidden-models-store', 'data'), State({"type": "dynamic-button", "index": MATCH}, "id"), ) def update_card(n_clicks, hidden_states, button_id): module_index = button_id['index'] # 更新当前模块的点击状态到存储 hidden_states[module_index] = n_clicks # 计算当前显示样式 style = {'display': 'none'} if n_clicks % 2 == 1 else {} return style, hidden_states
核心修改说明
dcc.Store使用storage_type='memory',确保每个用户会话拥有独立的状态存储,不会跨用户共享- 数据刷新回调接收Store的状态作为参数,生成组件时复用该状态,避免按钮点击次数重置
- 隐藏回调同步更新Store中的状态,保证页面刷新后组件状态与用户操作一致
- 组件生成时直接根据存储状态设置初始样式和点击次数,彻底解决刷新后状态丢失问题
内容的提问来源于stack exchange,提问作者Mike Lvov
相关产品推荐
相关产品推荐

