, key: string) => {
- acc[key] = {obj1: obj1[key], obj2: obj2[key]};
- return acc;
- },
- {},
- differingKeys as string[]
- );
-
- keysOnlyInObj2.forEach(key => {
- differences[key] = {obj1: undefined, obj2: obj2[key]};
- });
-
- return differences;
-};
-
type DashWrapperProps = {
/**
* Path of the component in the layout.
@@ -113,7 +72,7 @@ function DashWrapper({
// Select both the component and it's props.
// eslint-disable-next-line prefer-const
- let [component, componentProps, h, changedProps] = useSelector(
+ let [component, componentProps, h, changedProps, renderType] = useSelector(
selectDashProps(componentPath),
selectDashPropsEqualityFn
);
@@ -135,13 +94,6 @@ function DashWrapper({
}, [_newRender]);
/* eslint-enable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unused-vars */
- let propDifferences: any = {};
- if (memoizedProps.current) {
- propDifferences = findDifferences(
- memoizedProps.current,
- componentProps
- );
- }
memoizedProps.current = componentProps;
const setProps = (newProps: UpdatePropsPayload) => {
@@ -198,7 +150,8 @@ function DashWrapper({
props: changedProps,
itempath: componentPath,
component,
- config
+ config,
+ renderType: 'internal'
})
);
});
@@ -261,6 +214,8 @@ function DashWrapper({
const extraProps = {
setProps,
+ rendertype: newRender.current ? 'parent' : (
+ changedProps ? renderType : 'parent'),
...extras
};
@@ -277,15 +232,11 @@ function DashWrapper({
const childrenProp: string = childrenProps[i];
let childNewRender = 0;
if (
- childrenProp
- .split('.')[0]
- .replace('[]', '')
- .replace('{}', '') in propDifferences ||
childrenProp
.split('.')[0]
.replace('[]', '')
.replace('{}', '') in changedProps ||
- newRender.current
+ newRender.current || !h
) {
childNewRender = Date.now();
}
@@ -498,7 +449,7 @@ function DashWrapper({
hydratedChildren = wrapChildrenProp(
componentProps.children,
['children'],
- 'children' in propDifferences ||
+ !h ||
newRender.current ||
'children' in changedProps
? Date.now()
diff --git a/dash/dash-renderer/src/wrapper/selectors.ts b/dash/dash-renderer/src/wrapper/selectors.ts
index 5db8c8d194..ccf75d7cb7 100644
--- a/dash/dash-renderer/src/wrapper/selectors.ts
+++ b/dash/dash-renderer/src/wrapper/selectors.ts
@@ -1,7 +1,7 @@
import {DashLayoutPath, DashComponent, BaseDashProps} from '../types/component';
import {getComponentLayout, stringifyPath} from './wrapping';
-type SelectDashProps = [DashComponent, BaseDashProps, number, object];
+type SelectDashProps = [DashComponent, BaseDashProps, number, object, string];
export const selectDashProps =
(componentPath: DashLayoutPath) =>
@@ -15,11 +15,13 @@ export const selectDashProps =
const hash = state.layoutHashes[strPath];
let h = 0;
let changedProps: object = {};
+ let renderType: string = '';
if (hash) {
- h = hash[0];
- changedProps = hash[1];
+ h = hash['hash'];
+ changedProps = hash['changedProps'];
+ renderType = hash['renderType']
}
- return [c, c?.props, h, changedProps];
+ return [c, c?.props, h, changedProps, renderType];
};
export function selectDashPropsEqualityFn(
From 889b4a5ca6cb912211e12a7a39da5e1716a30a11 Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Mon, 31 Mar 2025 05:47:51 -0400
Subject: [PATCH 20/33] removed the new prop being passed down: `rendertype`
---
dash/dash-renderer/src/reducers/reducer.js | 11 +++++++++--
dash/dash-renderer/src/wrapper/DashWrapper.tsx | 11 ++++-------
dash/dash-renderer/src/wrapper/selectors.ts | 4 ++--
3 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/dash/dash-renderer/src/reducers/reducer.js b/dash/dash-renderer/src/reducers/reducer.js
index 0cafbd4be8..10dc7d936d 100644
--- a/dash/dash-renderer/src/reducers/reducer.js
+++ b/dash/dash-renderer/src/reducers/reducer.js
@@ -31,8 +31,15 @@ function adjustHashes(state, action) {
const actionPath = action.payload.itempath;
const strPath = stringifyPath(actionPath);
const prev = pathOr(0, [strPath, 'hash'], state);
- state = assoc(strPath, {hash: prev + 1,
- changedProps: action.payload.props, renderType: action.payload.renderType}, state);
+ state = assoc(
+ strPath,
+ {
+ hash: prev + 1,
+ changedProps: action.payload.props,
+ renderType: action.payload.renderType
+ },
+ state
+ );
return state;
}
diff --git a/dash/dash-renderer/src/wrapper/DashWrapper.tsx b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
index 04846b880a..89758eedfd 100644
--- a/dash/dash-renderer/src/wrapper/DashWrapper.tsx
+++ b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
@@ -72,7 +72,7 @@ function DashWrapper({
// Select both the component and it's props.
// eslint-disable-next-line prefer-const
- let [component, componentProps, h, changedProps, renderType] = useSelector(
+ let [component, componentProps, h, changedProps] = useSelector(
selectDashProps(componentPath),
selectDashPropsEqualityFn
);
@@ -214,8 +214,6 @@ function DashWrapper({
const extraProps = {
setProps,
- rendertype: newRender.current ? 'parent' : (
- changedProps ? renderType : 'parent'),
...extras
};
@@ -236,7 +234,8 @@ function DashWrapper({
.split('.')[0]
.replace('[]', '')
.replace('{}', '') in changedProps ||
- newRender.current || !h
+ newRender.current ||
+ !h
) {
childNewRender = Date.now();
}
@@ -449,9 +448,7 @@ function DashWrapper({
hydratedChildren = wrapChildrenProp(
componentProps.children,
['children'],
- !h ||
- newRender.current ||
- 'children' in changedProps
+ !h || newRender.current || 'children' in changedProps
? Date.now()
: 0
);
diff --git a/dash/dash-renderer/src/wrapper/selectors.ts b/dash/dash-renderer/src/wrapper/selectors.ts
index ccf75d7cb7..46930bc47e 100644
--- a/dash/dash-renderer/src/wrapper/selectors.ts
+++ b/dash/dash-renderer/src/wrapper/selectors.ts
@@ -15,11 +15,11 @@ export const selectDashProps =
const hash = state.layoutHashes[strPath];
let h = 0;
let changedProps: object = {};
- let renderType: string = '';
+ let renderType = '';
if (hash) {
h = hash['hash'];
changedProps = hash['changedProps'];
- renderType = hash['renderType']
+ renderType = hash['renderType'];
}
return [c, c?.props, h, changedProps, renderType];
};
From 9c8a62ad7aaee210af09f8ef3e1ca12c3eeb3b65 Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Mon, 31 Mar 2025 06:34:34 -0400
Subject: [PATCH 21/33] adding `renderType` but making it a conditional prop
that the component can subscribe to based upon its prop definitions
---
.../dash-renderer/src/wrapper/DashWrapper.tsx | 19 ++++++++++++++++---
dash/dash-renderer/src/wrapper/wrapping.ts | 17 ++++++++++++++++-
2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/dash/dash-renderer/src/wrapper/DashWrapper.tsx b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
index 89758eedfd..960d3f3dc6 100644
--- a/dash/dash-renderer/src/wrapper/DashWrapper.tsx
+++ b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
@@ -24,7 +24,12 @@ import {DashConfig} from '../config';
import {notifyObservers, onError, updateProps} from '../actions';
import {getWatchedKeys, stringifyId} from '../actions/dependencies';
import {recordUiEdit} from '../persistence';
-import {createElement, getComponentLayout, isDryComponent} from './wrapping';
+import {
+ createElement,
+ getComponentLayout,
+ isDryComponent,
+ checkRenderTypeProp
+} from './wrapping';
import Registry from '../registry';
import isSimpleComponent from '../isSimpleComponent';
import {
@@ -72,7 +77,7 @@ function DashWrapper({
// Select both the component and it's props.
// eslint-disable-next-line prefer-const
- let [component, componentProps, h, changedProps] = useSelector(
+ let [component, componentProps, h, changedProps, renderType] = useSelector(
selectDashProps(componentPath),
selectDashPropsEqualityFn
);
@@ -215,7 +220,15 @@ function DashWrapper({
const extraProps = {
setProps,
...extras
- };
+ } as {[key: string]: any};
+
+ if (checkRenderTypeProp(component)) {
+ extraProps['renderType'] = newRender.current
+ ? 'parent'
+ : changedProps
+ ? renderType
+ : 'parent';
+ }
const setHydratedProps = (component: any, componentProps: any) => {
// Hydrate components props
diff --git a/dash/dash-renderer/src/wrapper/wrapping.ts b/dash/dash-renderer/src/wrapper/wrapping.ts
index baf0e4f6f3..88aedd90d1 100644
--- a/dash/dash-renderer/src/wrapper/wrapping.ts
+++ b/dash/dash-renderer/src/wrapper/wrapping.ts
@@ -1,5 +1,5 @@
import React from 'react';
-import {mergeRight, path, type, has, join} from 'ramda';
+import {mergeRight, path, type, has, join, pathOr} from 'ramda';
import {DashComponent, DashLayoutPath} from '../types/component';
export function createElement(
@@ -61,3 +61,18 @@ export function getComponentLayout(
): DashComponent {
return path(componentPath, state.layout) as DashComponent;
}
+
+export function checkRenderTypeProp(componentDefinition: any) {
+ return (
+ 'renderType' in
+ pathOr(
+ {},
+ [
+ componentDefinition?.namespace,
+ componentDefinition?.type,
+ 'propTypes'
+ ],
+ window as any
+ )
+ );
+}
From ef41561c76b2442c62a0ec368ddefcd41512e56b Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Mon, 31 Mar 2025 09:44:06 -0400
Subject: [PATCH 22/33] adding test component and test for rendertype prop
---
.../src/components/RenderType.js | 21 ++++++
@plotly/dash-test-components/src/index.js | 2 +
.../integration/renderer/test_render_type.py | 67 +++++++++++++++++++
3 files changed, 90 insertions(+)
create mode 100644 @plotly/dash-test-components/src/components/RenderType.js
create mode 100644 tests/integration/renderer/test_render_type.py
diff --git a/@plotly/dash-test-components/src/components/RenderType.js b/@plotly/dash-test-components/src/components/RenderType.js
new file mode 100644
index 0000000000..1b402f15d2
--- /dev/null
+++ b/@plotly/dash-test-components/src/components/RenderType.js
@@ -0,0 +1,21 @@
+import React, { useState, useEffect } from "react";
+import PropTypes from "prop-types";
+
+const RenderType = (props) => {
+ const onClick = () => {
+ props.setProps({n_clicks: (props.n_clicks || 0) + 1})
+ }
+
+ return
+ {props.renderType}
+
+
;
+};
+
+RenderType.propTypes = {
+ id: PropTypes.string,
+ renderType: PropTypes.string,
+ n_clicks: PropTypes.number,
+ setProps: PropTypes.func
+};
+export default RenderType;
diff --git a/@plotly/dash-test-components/src/index.js b/@plotly/dash-test-components/src/index.js
index f72bfd0521..9a6523b22c 100644
--- a/@plotly/dash-test-components/src/index.js
+++ b/@plotly/dash-test-components/src/index.js
@@ -7,6 +7,7 @@ import MyPersistedComponentNested from './components/MyPersistedComponentNested'
import StyledComponent from './components/StyledComponent';
import WidthComponent from './components/WidthComponent';
import ComponentAsProp from './components/ComponentAsProp';
+import RenderType from './components/RenderType';
import DrawCounter from './components/DrawCounter';
import AddPropsComponent from "./components/AddPropsComponent";
@@ -32,4 +33,5 @@ export {
ShapeOrExactKeepOrderComponent,
ArrayOfExactOrShapeWithNodePropAssignNone,
ExternalComponent,
+ RenderType
};
diff --git a/tests/integration/renderer/test_render_type.py b/tests/integration/renderer/test_render_type.py
new file mode 100644
index 0000000000..93e0534455
--- /dev/null
+++ b/tests/integration/renderer/test_render_type.py
@@ -0,0 +1,67 @@
+import time
+from dash import Dash, Input, Output, html
+import json
+
+import dash_test_components as dt
+
+
+def test_rtype001_rendertype(dash_duo):
+ app = Dash()
+
+ app.layout = html.Div(
+ [
+ html.Div(
+ dt.RenderType(id="render_test"),
+ id="container",
+ ),
+ html.Button("redraw", id="redraw"),
+ html.Button("update render", id="update_render"),
+ html.Button("clientside", id="clientside_render"),
+ html.Div(id="render_output"),
+ ]
+ )
+
+ app.clientside_callback(
+ """(n) => {
+ dash_clientside.set_props('render_test', {n_clicks: 20})
+ }""",
+ Input("clientside_render", "n_clicks"),
+ )
+
+ @app.callback(
+ Output("container", "children"),
+ Input("redraw", "n_clicks"),
+ prevent_initial_call=True,
+ )
+ def on_click(_):
+ return dt.RenderType(id="render_test")
+
+ @app.callback(
+ Output("render_test", "n_clicks"),
+ Input("update_render", "n_clicks"),
+ prevent_initial_call=True,
+ )
+ def update_render(_):
+ return 0
+
+ @app.callback(Output("render_output", "children"), Input("render_test", "n_clicks"))
+ def display_clicks(n):
+ return json.dumps(n)
+
+ dash_duo.start_server(app)
+
+ render_type = "#render_test > span"
+ render_output = "#render_output"
+ dash_duo.wait_for_text_to_equal(render_type, "parent")
+ dash_duo.find_element("#update_render").click()
+ dash_duo.wait_for_text_to_equal(render_type, "callback")
+ dash_duo.wait_for_text_to_equal(render_output, "0")
+ dash_duo.find_element("#clientside_render").click()
+ dash_duo.wait_for_text_to_equal(render_type, "clientsideApi")
+ dash_duo.wait_for_text_to_equal(render_output, "20")
+ dash_duo.find_element("#render_test > button").click()
+ dash_duo.wait_for_text_to_equal(render_type, "internal")
+ dash_duo.wait_for_text_to_equal(render_output, "21")
+ dash_duo.find_element("#redraw").click()
+ dash_duo.wait_for_text_to_equal(render_type, "parent")
+ dash_duo.wait_for_text_to_equal(render_output, "null")
From c409895fa45439361d7ed55ac63b42b140f302c3 Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Mon, 31 Mar 2025 10:03:03 -0400
Subject: [PATCH 23/33] removing unused import
---
tests/integration/renderer/test_render_type.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/tests/integration/renderer/test_render_type.py b/tests/integration/renderer/test_render_type.py
index 93e0534455..17a6cfbae3 100644
--- a/tests/integration/renderer/test_render_type.py
+++ b/tests/integration/renderer/test_render_type.py
@@ -1,4 +1,3 @@
-import time
from dash import Dash, Input, Output, html
import json
From ce1da9ae321235fc56efed63238f016359410fce Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Mon, 31 Mar 2025 16:28:55 -0400
Subject: [PATCH 24/33] adjusting `renderType` to `dashRenderType` if the dev
wants to subscribe, they need to place on the component:
`namespace.component.dashRenderType = true`
---
@plotly/dash-test-components/src/components/RenderType.js | 6 ++++--
dash/dash-renderer/src/wrapper/DashWrapper.tsx | 2 +-
dash/dash-renderer/src/wrapper/wrapping.ts | 3 +--
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/@plotly/dash-test-components/src/components/RenderType.js b/@plotly/dash-test-components/src/components/RenderType.js
index 1b402f15d2..aa99f1feb8 100644
--- a/@plotly/dash-test-components/src/components/RenderType.js
+++ b/@plotly/dash-test-components/src/components/RenderType.js
@@ -7,15 +7,17 @@ const RenderType = (props) => {
}
return
- {props.renderType}
+ {props.dashRenderType}
;
};
RenderType.propTypes = {
id: PropTypes.string,
- renderType: PropTypes.string,
+ dashRenderType: PropTypes.string,
n_clicks: PropTypes.number,
setProps: PropTypes.func
};
+
+RenderType.dashRenderType = true;
export default RenderType;
diff --git a/dash/dash-renderer/src/wrapper/DashWrapper.tsx b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
index 960d3f3dc6..467c4d79b0 100644
--- a/dash/dash-renderer/src/wrapper/DashWrapper.tsx
+++ b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
@@ -223,7 +223,7 @@ function DashWrapper({
} as {[key: string]: any};
if (checkRenderTypeProp(component)) {
- extraProps['renderType'] = newRender.current
+ extraProps['dashRenderType'] = newRender.current
? 'parent'
: changedProps
? renderType
diff --git a/dash/dash-renderer/src/wrapper/wrapping.ts b/dash/dash-renderer/src/wrapper/wrapping.ts
index 88aedd90d1..9444d6191c 100644
--- a/dash/dash-renderer/src/wrapper/wrapping.ts
+++ b/dash/dash-renderer/src/wrapper/wrapping.ts
@@ -64,13 +64,12 @@ export function getComponentLayout(
export function checkRenderTypeProp(componentDefinition: any) {
return (
- 'renderType' in
+ 'dashRenderType' in
pathOr(
{},
[
componentDefinition?.namespace,
componentDefinition?.type,
- 'propTypes'
],
window as any
)
From f0db8c5cbc60216b12e7d8328881b9a52167272d Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Mon, 31 Mar 2025 16:36:15 -0400
Subject: [PATCH 25/33] replacing `Date.now()` with new object to force
rerender
---
dash/dash-renderer/src/wrapper/DashWrapper.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/dash/dash-renderer/src/wrapper/DashWrapper.tsx b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
index 467c4d79b0..54df206090 100644
--- a/dash/dash-renderer/src/wrapper/DashWrapper.tsx
+++ b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
@@ -241,7 +241,7 @@ function DashWrapper({
for (let i = 0; i < childrenProps.length; i++) {
const childrenProp: string = childrenProps[i];
- let childNewRender = 0;
+ let childNewRender: any = 0;
if (
childrenProp
.split('.')[0]
@@ -250,7 +250,7 @@ function DashWrapper({
newRender.current ||
!h
) {
- childNewRender = Date.now();
+ childNewRender = {};
}
const handleObject = (obj: any, opath: DashLayoutPath) => {
return mapObjIndexed(
@@ -462,7 +462,7 @@ function DashWrapper({
componentProps.children,
['children'],
!h || newRender.current || 'children' in changedProps
- ? Date.now()
+ ? {}
: 0
);
}
From 6588aac16149d1c7fb1bef35a25a5aac42ebc0b2 Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Mon, 31 Mar 2025 16:44:55 -0400
Subject: [PATCH 26/33] adjusting for lint
---
dash/dash-renderer/src/wrapper/wrapping.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dash/dash-renderer/src/wrapper/wrapping.ts b/dash/dash-renderer/src/wrapper/wrapping.ts
index 9444d6191c..0eb5ac4f6e 100644
--- a/dash/dash-renderer/src/wrapper/wrapping.ts
+++ b/dash/dash-renderer/src/wrapper/wrapping.ts
@@ -69,7 +69,7 @@ export function checkRenderTypeProp(componentDefinition: any) {
{},
[
componentDefinition?.namespace,
- componentDefinition?.type,
+ componentDefinition?.type
],
window as any
)
From 5e1bdf31b7f07a258d5c91a95394d999094468b6 Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Mon, 31 Mar 2025 17:00:36 -0400
Subject: [PATCH 27/33] adjustments from feedback
---
dash/dash-renderer/src/actions/callbacks.ts | 4 ---
.../src/observers/executedCallbacks.ts | 6 +---
dash/dash-renderer/src/reducers/reducer.js | 29 ++++++++-----------
.../src/utils/clientsideFunctions.ts | 6 +---
.../dash-renderer/src/wrapper/DashWrapper.tsx | 16 ++++------
dash/dash-renderer/src/wrapper/wrapping.ts | 5 +---
6 files changed, 20 insertions(+), 46 deletions(-)
diff --git a/dash/dash-renderer/src/actions/callbacks.ts b/dash/dash-renderer/src/actions/callbacks.ts
index 917e942ab0..8164063f4c 100644
--- a/dash/dash-renderer/src/actions/callbacks.ts
+++ b/dash/dash-renderer/src/actions/callbacks.ts
@@ -34,7 +34,6 @@ import {
CallbackResponseData,
SideUpdateOutput
} from '../types/callbacks';
-import {getComponentLayout} from '../wrapper/wrapping';
import {isMultiValued, stringifyId, isMultiOutputProp} from './dependencies';
import {urlBase} from './utils';
import {getCSRFHeader, dispatchError} from '.';
@@ -359,13 +358,10 @@ function updateComponent(component_id: any, props: any, cb: ICallbackPayload) {
// error.
return;
}
- const component = getComponentLayout(componentPath, _state);
dispatch(
updateProps({
props,
itempath: componentPath,
- component,
- config,
renderType: 'callback'
})
);
diff --git a/dash/dash-renderer/src/observers/executedCallbacks.ts b/dash/dash-renderer/src/observers/executedCallbacks.ts
index 3da7dda3d9..a090d9b7c3 100644
--- a/dash/dash-renderer/src/observers/executedCallbacks.ts
+++ b/dash/dash-renderer/src/observers/executedCallbacks.ts
@@ -34,7 +34,6 @@ import {ICallback, IStoredCallback} from '../types/callbacks';
import {updateProps, setPaths, handleAsyncError} from '../actions';
import {getPath, computePaths} from '../actions/paths';
-import {getComponentLayout} from '../wrapper/wrapping';
import {applyPersistence, prunePersistence} from '../persistence';
import {IStoreObserverDefinition} from '../StoreObserver';
@@ -47,7 +46,7 @@ const observer: IStoreObserverDefinition = {
function applyProps(id: any, updatedProps: any) {
const _state = getState();
- const {layout, paths, config} = _state;
+ const {layout, paths} = _state;
const itempath = getPath(paths, id);
if (!itempath) {
return false;
@@ -65,14 +64,11 @@ const observer: IStoreObserverDefinition = {
// In case the update contains whole components, see if any of
// those components have props to update to persist user edits.
const {props} = applyPersistence({props: updatedProps}, dispatch);
- const component = getComponentLayout(itempath, _state);
dispatch(
updateProps({
itempath,
props,
source: 'response',
- component,
- config,
renderType: 'callback'
})
);
diff --git a/dash/dash-renderer/src/reducers/reducer.js b/dash/dash-renderer/src/reducers/reducer.js
index 10dc7d936d..56d2c82ad2 100644
--- a/dash/dash-renderer/src/reducers/reducer.js
+++ b/dash/dash-renderer/src/reducers/reducer.js
@@ -27,22 +27,6 @@ export const apiRequests = [
'loginRequest'
];
-function adjustHashes(state, action) {
- const actionPath = action.payload.itempath;
- const strPath = stringifyPath(actionPath);
- const prev = pathOr(0, [strPath, 'hash'], state);
- state = assoc(
- strPath,
- {
- hash: prev + 1,
- changedProps: action.payload.props,
- renderType: action.payload.renderType
- },
- state
- );
- return state;
-}
-
const layoutHashes = (state = {}, action) => {
if (
includes(action.type, [
@@ -53,7 +37,18 @@ const layoutHashes = (state = {}, action) => {
) {
// Let us compare the paths sums to get updates without triggering
// render on the parent containers.
- return adjustHashes(state, action);
+ const actionPath = action.payload.itempath;
+ const strPath = stringifyPath(actionPath);
+ const prev = pathOr(0, [strPath, 'hash'], state);
+ state = assoc(
+ strPath,
+ {
+ hash: prev + 1,
+ changedProps: action.payload.props,
+ renderType: action.payload.renderType
+ },
+ state
+ );
}
return state;
};
diff --git a/dash/dash-renderer/src/utils/clientsideFunctions.ts b/dash/dash-renderer/src/utils/clientsideFunctions.ts
index 4c4d4f1428..fc22877d70 100644
--- a/dash/dash-renderer/src/utils/clientsideFunctions.ts
+++ b/dash/dash-renderer/src/utils/clientsideFunctions.ts
@@ -1,7 +1,6 @@
import {updateProps, notifyObservers} from '../actions/index';
import {getPath} from '../actions/paths';
import {getStores} from './stores';
-import {getComponentLayout} from '../wrapper/wrapping';
/**
* Set the props of a dash component by id or path.
@@ -18,19 +17,16 @@ function set_props(
const {dispatch, getState} = ds[y];
let componentPath;
const _state = getState();
- const {paths, config} = _state;
+ const {paths} = _state;
if (!Array.isArray(idOrPath)) {
componentPath = getPath(paths, idOrPath);
} else {
componentPath = idOrPath;
}
- const component = getComponentLayout(componentPath, _state);
dispatch(
updateProps({
props,
itempath: componentPath,
- component,
- config,
renderType: 'clientsideApi'
})
);
diff --git a/dash/dash-renderer/src/wrapper/DashWrapper.tsx b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
index 54df206090..ae127cc049 100644
--- a/dash/dash-renderer/src/wrapper/DashWrapper.tsx
+++ b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
@@ -63,8 +63,8 @@ type MemoizedPropsType = {
function DashWrapper({
componentPath,
_dashprivate_error,
- _passedComponent,
- _newRender,
+ _passedComponent, // pass component to the DashWrapper in the event that it is a newRender and there are no layouthashes
+ _newRender, // this is to force the component to newly render regardless of props (redraw and component as props) is passed from the parent
...extras
}: DashWrapperProps) {
const dispatch = useDispatch();
@@ -84,7 +84,7 @@ function DashWrapper({
/* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unused-vars */
// @ts-ignore
- const newlyRendered = useMemo(() => {
+ const newlyRendered: any = useMemo(() => {
if (_newRender) {
memoizedProps.current = {};
newRender.current = true;
@@ -154,8 +154,6 @@ function DashWrapper({
updateProps({
props: changedProps,
itempath: componentPath,
- component,
- config,
renderType: 'internal'
})
);
@@ -461,14 +459,12 @@ function DashWrapper({
hydratedChildren = wrapChildrenProp(
componentProps.children,
['children'],
- !h || newRender.current || 'children' in changedProps
- ? {}
- : 0
+ !h || newRender.current || 'children' in changedProps ? {} : 0
);
}
newRender.current = false;
- const rendered = config.props_check ? (
+ return config.props_check ? (
Date: Tue, 1 Apr 2025 10:14:21 -0400
Subject: [PATCH 28/33] adjusting for `state` no longer needed in dispatch
---
dash/dash-renderer/src/actions/callbacks.ts | 3 +--
dash/dash-renderer/src/observers/executedCallbacks.ts | 3 +--
dash/dash-renderer/src/utils/clientsideFunctions.ts | 3 +--
3 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/dash/dash-renderer/src/actions/callbacks.ts b/dash/dash-renderer/src/actions/callbacks.ts
index 8164063f4c..9765c8bba1 100644
--- a/dash/dash-renderer/src/actions/callbacks.ts
+++ b/dash/dash-renderer/src/actions/callbacks.ts
@@ -336,8 +336,7 @@ async function handleClientside(
function updateComponent(component_id: any, props: any, cb: ICallbackPayload) {
return function (dispatch: any, getState: any) {
- const _state = getState();
- const {paths, config} = _state;
+ const {paths, config} = getState();
const componentPath = getPath(paths, component_id);
if (!componentPath) {
if (!config.suppress_callback_exceptions) {
diff --git a/dash/dash-renderer/src/observers/executedCallbacks.ts b/dash/dash-renderer/src/observers/executedCallbacks.ts
index a090d9b7c3..f82a24bdf0 100644
--- a/dash/dash-renderer/src/observers/executedCallbacks.ts
+++ b/dash/dash-renderer/src/observers/executedCallbacks.ts
@@ -45,8 +45,7 @@ const observer: IStoreObserverDefinition = {
} = getState();
function applyProps(id: any, updatedProps: any) {
- const _state = getState();
- const {layout, paths} = _state;
+ const {layout, paths} = getState();
const itempath = getPath(paths, id);
if (!itempath) {
return false;
diff --git a/dash/dash-renderer/src/utils/clientsideFunctions.ts b/dash/dash-renderer/src/utils/clientsideFunctions.ts
index fc22877d70..4859df41bf 100644
--- a/dash/dash-renderer/src/utils/clientsideFunctions.ts
+++ b/dash/dash-renderer/src/utils/clientsideFunctions.ts
@@ -16,8 +16,7 @@ function set_props(
for (let y = 0; y < ds.length; y++) {
const {dispatch, getState} = ds[y];
let componentPath;
- const _state = getState();
- const {paths} = _state;
+ const {paths} = getState();
if (!Array.isArray(idOrPath)) {
componentPath = getPath(paths, idOrPath);
} else {
From 3ade208a8ad416b81da623927c0c298161e7eb39 Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Tue, 1 Apr 2025 10:53:29 -0400
Subject: [PATCH 29/33] adjustments based on feedback
---
.../dash-renderer/src/wrapper/DashWrapper.tsx | 63 ++++++++++---------
1 file changed, 34 insertions(+), 29 deletions(-)
diff --git a/dash/dash-renderer/src/wrapper/DashWrapper.tsx b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
index ae127cc049..16af483b6d 100644
--- a/dash/dash-renderer/src/wrapper/DashWrapper.tsx
+++ b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
@@ -71,33 +71,33 @@ function DashWrapper({
const memoizedKeys: MutableRefObject = useRef({});
const memoizedProps: MutableRefObject = useRef({});
const newRender = useRef(false);
+ let renderComponent: any = null;
+ let renderComponentProps: any = null;
+ let renderH: any = null;
// Get the config for the component as props
const config: DashConfig = useSelector(selectConfig);
- // Select both the component and it's props.
- // eslint-disable-next-line prefer-const
- let [component, componentProps, h, changedProps, renderType] = useSelector(
- selectDashProps(componentPath),
- selectDashPropsEqualityFn
- );
+ // Select component and it's props, along with render hash, changed props and the reason for render
+ const [component, componentProps, h, changedProps, renderType] =
+ useSelector(selectDashProps(componentPath), selectDashPropsEqualityFn);
+
+ renderComponent = component;
+ renderComponentProps = componentProps;
+ renderH = h;
- /* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unused-vars */
- // @ts-ignore
- const newlyRendered: any = useMemo(() => {
+ useMemo(() => {
if (_newRender) {
memoizedProps.current = {};
newRender.current = true;
- h = 0;
+ renderH = 0;
if (h in memoizedKeys.current) {
delete memoizedKeys.current[h];
}
} else {
newRender.current = false;
}
- return newRender.current;
}, [_newRender]);
- /* eslint-enable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unused-vars */
memoizedProps.current = componentProps;
@@ -163,8 +163,8 @@ function DashWrapper({
const createContainer = useCallback(
(container, containerPath, _childNewRender, key = undefined) => {
- if (isSimpleComponent(component)) {
- return component;
+ if (isSimpleComponent(container)) {
+ return container;
}
return (
{
if (newRender.current) {
- component = _passedComponent;
- componentProps = _passedComponent?.props;
+ renderComponent = _passedComponent;
+ renderComponentProps = _passedComponent?.props;
}
- if (!component) {
+ if (!renderComponent) {
return null;
}
- const element = Registry.resolve(component);
- const hydratedProps = setHydratedProps(component, componentProps);
+ const element = Registry.resolve(renderComponent);
+ const hydratedProps = setHydratedProps(
+ renderComponent,
+ renderComponentProps
+ );
let hydratedChildren: any;
- if (componentProps.children !== undefined) {
+ if (renderComponentProps.children !== undefined) {
hydratedChildren = wrapChildrenProp(
- componentProps.children,
+ renderComponentProps.children,
['children'],
- !h || newRender.current || 'children' in changedProps ? {} : 0
+ !renderH || newRender.current || 'children' in changedProps
+ ? {}
+ : 0
);
}
newRender.current = false;
@@ -468,7 +473,7 @@ function DashWrapper({
{createElement(
element,
@@ -483,14 +488,14 @@ function DashWrapper({
};
let hydrated = null;
- if (h in memoizedKeys.current && !newRender.current) {
- hydrated = React.isValidElement(memoizedKeys.current[h])
- ? memoizedKeys.current[h]
+ if (renderH in memoizedKeys.current && !newRender.current) {
+ hydrated = React.isValidElement(memoizedKeys.current[renderH])
+ ? memoizedKeys.current[renderH]
: null;
}
if (!hydrated) {
hydrated = hydrateFunc();
- memoizedKeys.current = {[h]: hydrated};
+ memoizedKeys.current = {[renderH]: hydrated};
}
return component ? (
From 6f744f0d1b842137247d67d8c75038461a09a674 Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Tue, 1 Apr 2025 11:15:20 -0400
Subject: [PATCH 30/33] reverting errant adjustment
---
dash/dash-renderer/src/wrapper/DashWrapper.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dash/dash-renderer/src/wrapper/DashWrapper.tsx b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
index 16af483b6d..62bdece5cc 100644
--- a/dash/dash-renderer/src/wrapper/DashWrapper.tsx
+++ b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
@@ -163,8 +163,8 @@ function DashWrapper({
const createContainer = useCallback(
(container, containerPath, _childNewRender, key = undefined) => {
- if (isSimpleComponent(container)) {
- return container;
+ if (isSimpleComponent(renderComponent)) {
+ return renderComponent;
}
return (
Date: Tue, 1 Apr 2025 11:16:42 -0400
Subject: [PATCH 31/33] additional `component` -> `renderComponent` adjustments
---
dash/dash-renderer/src/wrapper/DashWrapper.tsx | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/dash/dash-renderer/src/wrapper/DashWrapper.tsx b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
index 62bdece5cc..5a87ed7a24 100644
--- a/dash/dash-renderer/src/wrapper/DashWrapper.tsx
+++ b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
@@ -498,13 +498,13 @@ function DashWrapper({
memoizedKeys.current = {[renderH]: hydrated};
}
- return component ? (
+ return renderComponent ? (
Date: Tue, 1 Apr 2025 12:13:00 -0400
Subject: [PATCH 32/33] adjusting for missing variable swaps and removing
unused `memoizeProps`
---
dash/dash-renderer/src/wrapper/DashWrapper.tsx | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/dash/dash-renderer/src/wrapper/DashWrapper.tsx b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
index 5a87ed7a24..82bee5b0ba 100644
--- a/dash/dash-renderer/src/wrapper/DashWrapper.tsx
+++ b/dash/dash-renderer/src/wrapper/DashWrapper.tsx
@@ -55,11 +55,6 @@ type MemoizedKeysType = {
[key: string]: React.ReactNode | null; // This includes React elements, strings, numbers, etc.
};
-// Define a type for the memoized props
-type MemoizedPropsType = {
- [key: string]: any;
-};
-
function DashWrapper({
componentPath,
_dashprivate_error,
@@ -69,7 +64,6 @@ function DashWrapper({
}: DashWrapperProps) {
const dispatch = useDispatch();
const memoizedKeys: MutableRefObject = useRef({});
- const memoizedProps: MutableRefObject = useRef({});
const newRender = useRef(false);
let renderComponent: any = null;
let renderComponentProps: any = null;
@@ -88,21 +82,18 @@ function DashWrapper({
useMemo(() => {
if (_newRender) {
- memoizedProps.current = {};
newRender.current = true;
renderH = 0;
- if (h in memoizedKeys.current) {
- delete memoizedKeys.current[h];
+ if (renderH in memoizedKeys.current) {
+ delete memoizedKeys.current[renderH];
}
} else {
newRender.current = false;
}
}, [_newRender]);
- memoizedProps.current = componentProps;
-
const setProps = (newProps: UpdatePropsPayload) => {
- const {id} = componentProps;
+ const {id} = renderComponentProps;
const {_dash_error, ...restProps} = newProps;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -137,7 +128,7 @@ function DashWrapper({
batch(() => {
// setProps here is triggered by the UI - record these changes
// for persistence
- recordUiEdit(component, newProps, dispatch);
+ recordUiEdit(renderComponent, newProps, dispatch);
// Only dispatch changes to Dash if a watched prop changed
if (watchedKeys.length) {
From cde9acf82dbc0834e937109d9bbb1bacd776cde5 Mon Sep 17 00:00:00 2001
From: BSd3v <82055130+BSd3v@users.noreply.github.com>
Date: Tue, 1 Apr 2025 12:28:17 -0400
Subject: [PATCH 33/33] updating change log entry
---
CHANGELOG.md | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c5b3b2c6db..6681cbaef6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,11 +6,17 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## Changed
- [#3113](https://github.com/plotly/dash/pull/3113) Adjusted background polling requests to strip the data from the request, this allows for context to flow as normal. This addresses issue [#3111](https://github.com/plotly/dash/pull/3111)
-- [#3248] changes to the rendering logic
+- [#3248](https://github.com/plotly/dash/pull/3248) Changes to rendering logic:
+ - if it is first time rendering, render from the parent props
+ - listens only to updates for that single component, no children listening to parents
+ - if parents change a prop with components as props, only the prop changed re-renders, this is then forced on all children regardless of whether or not the props changed
## Fixed
- [#3251](https://github.com/plotly/dash/pull/3251). Prevented default styles from overriding `className_*` props in `dcc.Upload` component.
+## Added
+- [#3248](https://github.com/plotly/dash/pull/3248) added new `dashRenderType` to determine why the component layout was changed (`internal`, `callback`, `parent`, `clientsideApi`):
+ - this can be utilized to keep from rendering components by the component having `dashRenderType` defined as a prop, and the `dashRenderType = true` must be set on the component, eg (`Div.dashRenderType = true`)
## [3.0.1] - 2025-03-24