diff --git a/dash/dash-renderer/src/actions/constants.js b/dash/dash-renderer/src/actions/constants.js index 9c744f1655..47406b6627 100644 --- a/dash/dash-renderer/src/actions/constants.js +++ b/dash/dash-renderer/src/actions/constants.js @@ -10,7 +10,8 @@ const actionList = { ON_ERROR: 1, SET_HOOKS: 1, INSERT_COMPONENT: 1, - REMOVE_COMPONENT: 1 + REMOVE_COMPONENT: 1, + RESET_COMPONENT_STATE: 1 }; export const getAction = action => { diff --git a/dash/dash-renderer/src/actions/index.js b/dash/dash-renderer/src/actions/index.js index 5df75d708e..7c92d17afc 100644 --- a/dash/dash-renderer/src/actions/index.js +++ b/dash/dash-renderer/src/actions/index.js @@ -22,6 +22,9 @@ export const insertComponent = createAction(getAction('INSERT_COMPONENT')); export const removeComponent = createAction(getAction('REMOVE_COMPONENT')); export const onPropChange = createAction(getAction('ON_PROP_CHANGE')); +export const resetComponentState = createAction( + getAction('RESET_COMPONENT_STATE') +); export function updateProps(payload) { return (dispatch, getState) => { diff --git a/dash/dash-renderer/src/reducers/reducer.js b/dash/dash-renderer/src/reducers/reducer.js index 56d2c82ad2..759c198989 100644 --- a/dash/dash-renderer/src/reducers/reducer.js +++ b/dash/dash-renderer/src/reducers/reducer.js @@ -49,6 +49,17 @@ const layoutHashes = (state = {}, action) => { }, state ); + } else if (action.type === 'RESET_COMPONENT_STATE') { + const {itempath} = action.payload; + if (itempath) { + const prefixStr = stringifyPath(itempath); + // Remove all hashes for keys starting with prefixStr + return Object.fromEntries( + Object.entries(state).filter( + ([key]) => !key.startsWith(prefixStr) + ) + ); + } } return state; }; diff --git a/dash/dash-renderer/src/wrapper/DashWrapper.tsx b/dash/dash-renderer/src/wrapper/DashWrapper.tsx index 57e152ebcc..8a017ed09d 100644 --- a/dash/dash-renderer/src/wrapper/DashWrapper.tsx +++ b/dash/dash-renderer/src/wrapper/DashWrapper.tsx @@ -1,4 +1,10 @@ -import React, {useCallback, MutableRefObject, useRef, useMemo} from 'react'; +import React, { + useCallback, + MutableRefObject, + useRef, + useMemo, + useEffect +} from 'react'; import { path, concat, @@ -21,7 +27,12 @@ import {useSelector, useDispatch, batch} from 'react-redux'; import ComponentErrorBoundary from '../components/error/ComponentErrorBoundary.react'; import {DashLayoutPath, UpdatePropsPayload} from '../types/component'; import {DashConfig} from '../config'; -import {notifyObservers, onError, updateProps} from '../actions'; +import { + notifyObservers, + onError, + updateProps, + resetComponentState +} from '../actions'; import {getWatchedKeys, stringifyId} from '../actions/dependencies'; import { createElement, @@ -65,6 +76,7 @@ function DashWrapper({ const dispatch = useDispatch(); const memoizedKeys: MutableRefObject = useRef({}); const newRender = useRef(false); + const freshRenders = useRef(0); const renderedPath = useRef(componentPath); let renderComponent: any = null; let renderComponentProps: any = null; @@ -85,6 +97,7 @@ function DashWrapper({ if (_newRender) { newRender.current = true; renderH = 0; + freshRenders.current += 1; if (renderH in memoizedKeys.current) { delete memoizedKeys.current[renderH]; } @@ -432,6 +445,16 @@ function DashWrapper({ return props; }; + useEffect(() => { + if (_newRender) { + dispatch( + resetComponentState({ + itempath: componentPath + }) + ); + } + }, [_newRender]); + const hydrateFunc = () => { if (newRender.current) { renderComponent = _passedComponent; @@ -498,6 +521,7 @@ function DashWrapper({ } error={_dashprivate_error} dispatch={dispatch} + key={freshRenders.current} > {React.isValidElement(hydrated) ? hydrated :
}