diff --git a/packages/form-js-viewer/src/features/repeatRender/RepeatRenderManager.js b/packages/form-js-viewer/src/features/repeatRender/RepeatRenderManager.js index ddb3fef5b..5f2454f1e 100644 --- a/packages/form-js-viewer/src/features/repeatRender/RepeatRenderManager.js +++ b/packages/form-js-viewer/src/features/repeatRender/RepeatRenderManager.js @@ -80,10 +80,10 @@ export default class RepeatRenderManager { return ( <> {displayValues.map((value, index) => { - const elementProps = { + const elementProps = useMemo(() => ({ ...restProps, - indexes: { ...(indexes || {}), [ repeaterField.id ]: index }, - }; + indexes: { ...(indexes || {}), [ repeaterField.id ]: index } + }), [ index ]); const localExpressionContextInfo = useMemo(() => ({ data: parentExpressionContextInfo.data, diff --git a/packages/form-js-viewer/src/render/components/FormField.js b/packages/form-js-viewer/src/render/components/FormField.js index 971f1c7f8..ebd5b2bc1 100644 --- a/packages/form-js-viewer/src/render/components/FormField.js +++ b/packages/form-js-viewer/src/render/components/FormField.js @@ -77,7 +77,7 @@ export default function FormField(props) { if (viewerCommands && initialValue) { viewerCommands.updateFieldValidation(field, initialValue, indexes); } - }, [ viewerCommands, field, initialValue, JSON.stringify(indexes) ]); + }, [ viewerCommands, field, initialValue, indexes ]); const hidden = useCondition(field.conditional && field.conditional.hide || null); diff --git a/packages/form-js-viewer/src/render/components/form-fields/Taglist.js b/packages/form-js-viewer/src/render/components/form-fields/Taglist.js index 790c19c12..af7e0d5cc 100644 --- a/packages/form-js-viewer/src/render/components/form-fields/Taglist.js +++ b/packages/form-js-viewer/src/render/components/form-fields/Taglist.js @@ -1,6 +1,6 @@ import { useMemo, useRef, useState } from 'preact/hooks'; -import { useService } from '../../hooks'; +import { useDeepCompareState, useService } from '../../hooks'; import useOptionsAsync, { LOAD_STATES } from '../../hooks/useOptionsAsync'; import useCleanupMultiSelectValues from '../../hooks/useCleanupMultiSelectValues'; import { useGetLabelCorrelation } from '../../hooks/useGetLabelCorrelation'; @@ -35,7 +35,7 @@ export default function Taglist(props) { onBlur, field, readonly, - value : values = [] + value } = props; const { @@ -58,6 +58,9 @@ export default function Taglist(props) { options } = useOptionsAsync(field); + // ensures we render based on array content instead of reference + const values = useDeepCompareState(value || [], []); + useCleanupMultiSelectValues({ field, loadState, @@ -70,7 +73,6 @@ export default function Taglist(props) { const hasOptionsLeft = useMemo(() => options.length > values.length, [ options.length, values.length ]); - // Usage of stringify is necessary here because we want this effect to only trigger when there is a value change to the array const filteredOptions = useMemo(() => { if (loadState !== LOAD_STATES.LOADED) { return []; @@ -82,7 +84,7 @@ export default function Taglist(props) { }; return options.filter(isValidFilteredOption); - }, [ filter, options, JSON.stringify(values), loadState ]); + }, [ filter, options, values, loadState ]); const selectValue = (value) => { diff --git a/packages/form-js-viewer/src/render/components/form-fields/parts/Datepicker.js b/packages/form-js-viewer/src/render/components/form-fields/parts/Datepicker.js index 7f53c711b..2179b3f35 100644 --- a/packages/form-js-viewer/src/render/components/form-fields/parts/Datepicker.js +++ b/packages/form-js-viewer/src/render/components/form-fields/parts/Datepicker.js @@ -6,6 +6,7 @@ import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; import CalendarIcon from '../icons/Calendar.svg'; import InputAdorner from './InputAdorner'; import Label from '../../Label'; +import { useDeepCompareState } from '../../../hooks'; export default function Datepicker(props) { @@ -18,7 +19,7 @@ export default function Datepicker(props) { required, disabled, disallowPassedDates, - date, + date: dateObject, readonly, setDate } = props; @@ -30,6 +31,9 @@ export default function Datepicker(props) { const [ isInputDirty, setIsInputDirty ] = useState(false); const [ forceFocusCalendar, setForceFocusCalendar ] = useState(false); + // ensures we render based on date value instead of reference + const date = useDeepCompareState(dateObject, null); + // shorts the date value back to the source useEffect(() => { @@ -38,7 +42,7 @@ export default function Datepicker(props) { flatpickrInstance.setDate(date, true); setIsInputDirty(false); - }, [ flatpickrInstance, date.toString() ]); + }, [ flatpickrInstance, date ]); useEffect(() => { diff --git a/packages/form-js-viewer/src/render/hooks/useCleanupMultiSelectValues.js b/packages/form-js-viewer/src/render/hooks/useCleanupMultiSelectValues.js index 5c8bbde32..0f65f5587 100644 --- a/packages/form-js-viewer/src/render/hooks/useCleanupMultiSelectValues.js +++ b/packages/form-js-viewer/src/render/hooks/useCleanupMultiSelectValues.js @@ -1,6 +1,7 @@ import { useEffect } from 'preact/hooks'; import { LOAD_STATES } from './useOptionsAsync'; import { hasEqualValue } from '../components/util/sanitizerUtil'; +import useDeepCompareState from './useDeepCompareState'; export default function(props) { @@ -9,10 +10,12 @@ export default function(props) { options, loadState, onChange, - values + values: valuesArray } = props; - // Ensures that the values are always a subset of the possible options + const values = useDeepCompareState(valuesArray, []); + + // ensures that the values are always a subset of the possible options useEffect(() => { if (loadState !== LOAD_STATES.LOADED) { @@ -29,6 +32,6 @@ export default function(props) { }); } - }, [ field, options, onChange, JSON.stringify(values), loadState ]); + }, [ field, options, onChange, values, loadState ]); } \ No newline at end of file diff --git a/packages/form-js-viewer/src/render/hooks/useDeepCompareState.js b/packages/form-js-viewer/src/render/hooks/useDeepCompareState.js index b33e3598c..4cb30e943 100644 --- a/packages/form-js-viewer/src/render/hooks/useDeepCompareState.js +++ b/packages/form-js-viewer/src/render/hooks/useDeepCompareState.js @@ -4,6 +4,7 @@ import { } from 'preact/hooks'; import usePrevious from './usePrevious'; +import isEqual from 'lodash/isEqual'; /** * A custom hook to manage state changes with deep comparison. @@ -18,7 +19,7 @@ export default function useDeepCompareState(value, defaultValue) { const previous = usePrevious(value, defaultValue, [ value ]); - const changed = !compare(previous, value); + const changed = !isEqual(previous, value); useEffect(() => { if (changed) { @@ -27,11 +28,4 @@ export default function useDeepCompareState(value, defaultValue) { }, [ changed, value ]); return state; - -} - -// helpers ////////////////////////// - -function compare(a, b) { - return JSON.stringify(a) === JSON.stringify(b); -} +} \ No newline at end of file