diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0ddef7d826..221f8979df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- [#3284](https://github.com/plotly/dash/pull/3284) Fix component as props having the same key when used in the same container.
- [#3287](https://github.com/plotly/dash/pull/3287) Fix typing component generation & explicitize_args.
- [#3282](https://github.com/plotly/dash/pull/3282) Fix incorrect cancellation of pattern matched long callbacks.
+- [#3289](https://github.com/plotly/dash/pull/3289) Fixed issue with debugTitle where status doesnt exist and allow_duplicates to ignore the hash for prop loading in the target.
## [3.0.3] - 2025-04-14
diff --git a/components/dash-core-components/src/fragments/Loading/spinners/CircleSpinner.jsx b/components/dash-core-components/src/fragments/Loading/spinners/CircleSpinner.jsx
index 19c61411e5..1a486d77f1 100644
--- a/components/dash-core-components/src/fragments/Loading/spinners/CircleSpinner.jsx
+++ b/components/dash-core-components/src/fragments/Loading/spinners/CircleSpinner.jsx
@@ -16,7 +16,7 @@ const CircleSpinner = ({
style,
}) => {
let debugTitle;
- if (debug) {
+ if (debug && status) {
debugTitle = status.map((s) => );
}
let spinnerClass = fullscreen ? 'dash-spinner-container' : '';
diff --git a/components/dash-core-components/src/fragments/Loading/spinners/CubeSpinner.jsx b/components/dash-core-components/src/fragments/Loading/spinners/CubeSpinner.jsx
index bb44c0bbd7..936ec5fd8c 100644
--- a/components/dash-core-components/src/fragments/Loading/spinners/CubeSpinner.jsx
+++ b/components/dash-core-components/src/fragments/Loading/spinners/CubeSpinner.jsx
@@ -7,7 +7,7 @@ import DebugTitle from './DebugTitle.jsx';
const CubeSpinner = ({status, color, fullscreen, debug, className, style}) => {
let debugTitle;
- if (debug) {
+ if (debug && status) {
debugTitle = status.map((s) => );
}
let spinnerClass = fullscreen ? 'dash-spinner-container' : '';
diff --git a/components/dash-core-components/src/fragments/Loading/spinners/DefaultSpinner.jsx b/components/dash-core-components/src/fragments/Loading/spinners/DefaultSpinner.jsx
index 2ec51bc9a5..0d9c7d4924 100644
--- a/components/dash-core-components/src/fragments/Loading/spinners/DefaultSpinner.jsx
+++ b/components/dash-core-components/src/fragments/Loading/spinners/DefaultSpinner.jsx
@@ -15,7 +15,7 @@ const DefaultSpinner = ({
style,
}) => {
let debugTitle;
- if (debug) {
+ if (debug && status) {
debugTitle = status.map((s) => );
}
let spinnerClass = fullscreen ? 'dash-spinner-container' : '';
diff --git a/components/dash-core-components/src/fragments/Loading/spinners/DotSpinner.jsx b/components/dash-core-components/src/fragments/Loading/spinners/DotSpinner.jsx
index 1ea1dc1c25..c6a0906449 100644
--- a/components/dash-core-components/src/fragments/Loading/spinners/DotSpinner.jsx
+++ b/components/dash-core-components/src/fragments/Loading/spinners/DotSpinner.jsx
@@ -8,7 +8,7 @@ import DebugTitle from './DebugTitle.jsx';
*/
const DotSpinner = ({status, color, fullscreen, debug, className, style}) => {
let debugTitle;
- if (debug) {
+ if (debug && status) {
debugTitle = status.map((s) => );
}
let spinnerClass = fullscreen ? 'dash-spinner-container' : '';
diff --git a/components/dash-core-components/src/fragments/Loading/spinners/GraphSpinner.jsx b/components/dash-core-components/src/fragments/Loading/spinners/GraphSpinner.jsx
index f4a4e06b32..f79ff2ac83 100644
--- a/components/dash-core-components/src/fragments/Loading/spinners/GraphSpinner.jsx
+++ b/components/dash-core-components/src/fragments/Loading/spinners/GraphSpinner.jsx
@@ -5,7 +5,7 @@ import DebugTitle from './DebugTitle.jsx';
const GraphSpinner = ({status, fullscreen, debug, className, style}) => {
let debugTitle;
- if (debug) {
+ if (debug && status) {
debugTitle = status.map((s) => );
}
let spinnerClass = fullscreen ? 'dash-spinner-container' : '';
diff --git a/components/dash-core-components/tests/integration/loading/test_loading_component.py b/components/dash-core-components/tests/integration/loading/test_loading_component.py
index a7500daeba..597bc396ff 100644
--- a/components/dash-core-components/tests/integration/loading/test_loading_component.py
+++ b/components/dash-core-components/tests/integration/loading/test_loading_component.py
@@ -689,3 +689,69 @@ def updateDiv(n_clicks):
dash_dcc.wait_for_text_to_equal("#div-1", "changed")
assert dash_dcc.get_logs() == []
+
+
+# multiple components, only one triggers the spinner
+def test_ldcp017_loading_component_target_components_duplicates(dash_dcc):
+
+ lock = Lock()
+
+ app = Dash(__name__)
+
+ app.layout = html.Div(
+ [
+ dcc.Loading(
+ [
+ html.Button(id="btn-1"),
+ html.Button(id="btn-2", children="content 2"),
+ ],
+ className="loading-1",
+ target_components={"btn-2": "children"},
+ debug=True,
+ )
+ ],
+ id="root",
+ )
+
+ @app.callback(Output("btn-1", "children"), [Input("btn-2", "n_clicks")])
+ def updateDiv1(n_clicks):
+ if n_clicks:
+ with lock:
+ return "changed 1"
+
+ return "content 1"
+
+ @app.callback(
+ Output("btn-2", "children", allow_duplicate=True),
+ [Input("btn-1", "n_clicks")],
+ prevent_initial_call=True,
+ )
+ def updateDiv2(n_clicks):
+ if n_clicks:
+ with lock:
+ return "changed 2"
+
+ return "content 2"
+
+ dash_dcc.start_server(app)
+
+ dash_dcc.wait_for_text_to_equal("#btn-1", "content 1")
+ dash_dcc.wait_for_text_to_equal("#btn-2", "content 2")
+
+ with lock:
+ dash_dcc.find_element("#btn-1").click()
+
+ dash_dcc.find_element(".loading-1 .dash-spinner")
+ dash_dcc.wait_for_text_to_equal("#btn-2", "")
+
+ dash_dcc.wait_for_text_to_equal("#btn-2", "changed 2")
+
+ with lock:
+ dash_dcc.find_element("#btn-2").click()
+ spinners = dash_dcc.find_elements(".loading-1 .dash-spinner")
+ dash_dcc.wait_for_text_to_equal("#btn-1", "")
+
+ dash_dcc.wait_for_text_to_equal("#btn-1", "changed 1")
+ assert spinners == []
+
+ assert dash_dcc.get_logs() == []
diff --git a/dash/dash-renderer/src/actions/callbacks.ts b/dash/dash-renderer/src/actions/callbacks.ts
index 4cbd6a3d04..82c7211446 100644
--- a/dash/dash-renderer/src/actions/callbacks.ts
+++ b/dash/dash-renderer/src/actions/callbacks.ts
@@ -748,7 +748,7 @@ export function executeCallback(
const __execute = async (): Promise => {
const loadingOutputs = outputs.map(out => ({
path: getPath(paths, out.id),
- property: out.property,
+ property: out.property?.split('@')[0],
id: out.id
}));
dispatch(loading(loadingOutputs));