Skip to content

Commit 8bebf83

Browse files
committed
Fix set_props with children not having prop value in callbacks
1 parent 39a002f commit 8bebf83

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

dash/dash-renderer/src/actions/callbacks.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ import {
3636
} from '../types/callbacks';
3737
import {isMultiValued, stringifyId, isMultiOutputProp} from './dependencies';
3838
import {urlBase} from './utils';
39-
import {getCSRFHeader, dispatchError} from '.';
39+
import {getCSRFHeader, dispatchError, setPaths} from '.';
4040
import {createAction, Action} from 'redux-actions';
4141
import {addHttpHeaders} from '../actions';
4242
import {notifyObservers, updateProps} from './index';
4343
import {CallbackJobPayload} from '../reducers/callbackJobs';
4444
import {handlePatch, isPatch} from './patch';
45-
import {getPath} from './paths';
45+
import {computePaths, getPath} from './paths';
4646

4747
import {requestDependencies} from './requestDependencies';
4848

@@ -51,6 +51,7 @@ import {loadLibrary} from '../utils/libraries';
5151
import {parsePMCId} from './patternMatching';
5252
import {replacePMC} from './patternMatching';
5353
import {loaded, loading} from './loading';
54+
import {getComponentLayout} from '../wrapper/wrapping';
5455

5556
export const addBlockedCallbacks = createAction<IBlockedCallback[]>(
5657
CallbackActionType.AddBlocked
@@ -409,7 +410,25 @@ function sideUpdate(outputs: SideUpdateOutput, cb: ICallbackPayload) {
409410
return acc;
410411
}, [] as any[])
411412
.forEach(([id, idProps]) => {
413+
const state = getState();
412414
dispatch(updateComponent(id, idProps, cb));
415+
416+
const componentPath = getPath(state.paths, id);
417+
const oldComponent = getComponentLayout(componentPath, state);
418+
419+
dispatch(
420+
setPaths(
421+
computePaths(
422+
{
423+
...oldComponent,
424+
props: {...oldComponent.props, ...idProps}
425+
},
426+
[...componentPath],
427+
state.paths,
428+
state.paths.events
429+
)
430+
)
431+
);
413432
});
414433
};
415434
}

tests/integration/callbacks/test_arbitrary_callbacks.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,44 @@ def test_arb007_clientside_no_output(dash_duo):
232232
dash_duo.wait_for_text_to_equal("#output", "start1")
233233
dash_duo.find_element("#start2").click()
234234
dash_duo.wait_for_text_to_equal("#output", "start2")
235+
236+
237+
def test_arb008_set_props_chain_cb(dash_duo):
238+
app = Dash(suppress_callback_exceptions=True)
239+
240+
app.layout = html.Div(
241+
[
242+
html.Button("origin button", id="origin-button"),
243+
html.Div(id="generated-button-container"),
244+
html.Div("initial text", id="generated-button-output"),
245+
],
246+
style={"padding": 50},
247+
)
248+
249+
@app.callback(
250+
Input("origin-button", "n_clicks"),
251+
)
252+
def generate_button(n_clicks):
253+
set_props(
254+
"generated-button-container",
255+
{
256+
"children": html.Button(
257+
"generated button", id="generated-button", n_clicks=0
258+
)
259+
},
260+
)
261+
262+
@app.callback(
263+
Output("generated-button-output", "children"),
264+
Input("generated-button", "n_clicks", allow_optional=True),
265+
prevent_initial_call=True,
266+
)
267+
def update_output(n_clicks):
268+
return f"n_clicks: {n_clicks}"
269+
270+
dash_duo.start_server(app)
271+
272+
dash_duo.wait_for_element("#origin-button").click()
273+
for i in range(1, 5):
274+
dash_duo.wait_for_element("#generated-button").click()
275+
dash_duo.wait_for_text_to_equal("#generated-button-output", f"n_clicks: {i}")

0 commit comments

Comments
 (0)