Skip to content

Commit 1b673e3

Browse files
committed
fix multiple concurrnt loading states
1 parent e362444 commit 1b673e3

File tree

2 files changed

+69
-13
lines changed

2 files changed

+69
-13
lines changed

dash-renderer/src/observers/loadingMap.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
equals,
33
flatten,
4-
forEach,
54
isEmpty,
65
map,
76
reduce
@@ -43,26 +42,22 @@ const observer: IStoreObserverDefinition<IStoreState> = {
4342
const nextMap: any = isEmpty(loadingPaths) ?
4443
null :
4544
reduce(
46-
(res, path) => {
45+
(res, {id, property, path}) => {
4746
let target = res;
48-
const idprop = {
49-
id: path.id,
50-
property: path.property
51-
};
47+
const idprop = {id, property};
5248

5349
// Assign all affected props for this path and nested paths
5450
target.__dashprivate__idprops__ = target.__dashprivate__idprops__ || [];
5551
target.__dashprivate__idprops__.push(idprop);
5652

57-
forEach(p => {
58-
target = (target[p] =
59-
target[p] ??
60-
p === 'children' ? [] : {}
61-
)
53+
path.forEach((p, i) => {
54+
target = (target[p] = target[p] ??
55+
(p === 'children' && typeof path[i + 1] === 'number' ? [] : {})
56+
);
6257

6358
target.__dashprivate__idprops__ = target.__dashprivate__idprops__ || [];
6459
target.__dashprivate__idprops__.push(idprop);
65-
}, path.path);
60+
});
6661

6762
// Assign one affected prop for this path
6863
target.__dashprivate__idprop__ = target.__dashprivate__idprop__ || idprop;

tests/integration/renderer/test_multi_output.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from multiprocessing import Value
1+
from multiprocessing import Value, Lock
22

33
import dash
44
from dash.dependencies import Input, Output
@@ -164,3 +164,64 @@ def test_rdmo005_set_props_behavior(dash_duo):
164164

165165
dash_duo.find_element("#container input").send_keys("hello input w/o ID")
166166
dash_duo.wait_for_text_to_equal("#container input", "hello input w/o ID")
167+
168+
169+
def test_rdmo006_multi_loading_components(dash_duo):
170+
lock = Lock()
171+
172+
app = dash.Dash(__name__)
173+
174+
app.layout = html.Div(
175+
children=[
176+
html.H3("Edit text input to see loading state"),
177+
dcc.Input(id="input-3", value='Input triggers the loading states'),
178+
dcc.Loading(className="loading-1", children=[
179+
html.Div(id="loading-output-1")
180+
], type="default"),
181+
html.Div(
182+
[
183+
dcc.Loading(
184+
className="loading-2",
185+
children=[html.Div([html.Div(id="loading-output-2")])],
186+
type="circle",
187+
),
188+
dcc.Loading(
189+
className="loading-3",
190+
children=dcc.Graph(id='graph'),
191+
type="cube",
192+
)
193+
]
194+
),
195+
],
196+
)
197+
198+
@app.callback(
199+
[
200+
Output("graph", "figure"),
201+
Output("loading-output-1", "children"),
202+
Output("loading-output-2", "children"),
203+
],
204+
[Input("input-3", "value")])
205+
def input_triggers_nested(value):
206+
with lock:
207+
return dict(data=[dict(y=[1, 4, 2, 3])]), value, value
208+
209+
def wait_for_all_spinners():
210+
dash_duo.find_element('.loading-1 .dash-spinner.dash-default-spinner')
211+
dash_duo.find_element('.loading-2 .dash-spinner.dash-sk-circle')
212+
dash_duo.find_element('.loading-3 .dash-spinner.dash-cube-container')
213+
214+
def wait_for_no_spinners():
215+
dash_duo.wait_for_no_elements('.dash-spinner')
216+
217+
with lock:
218+
dash_duo.start_server(app)
219+
wait_for_all_spinners()
220+
221+
wait_for_no_spinners()
222+
223+
with lock:
224+
dash_duo.find_element('#input-3').send_keys('X')
225+
wait_for_all_spinners()
226+
227+
wait_for_no_spinners()

0 commit comments

Comments
 (0)