Skip to content

Commit

Permalink
fix: simplify deep compare and useOptionsAsync
Browse files Browse the repository at this point in the history
Related to #1067
  • Loading branch information
Skaiir committed Feb 22, 2024
1 parent 6738227 commit c6e7a6e
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo, useRef, useState } from 'preact/hooks';

import {
useDeepCompareState,
useDeepCompareMemoize,
useService,
useOptionsAsync,
useCleanupMultiSelectValue,
Expand Down Expand Up @@ -57,7 +57,7 @@ export function Taglist(props) {
} = useOptionsAsync(field);

// ensures we render based on array content instead of reference
const values = useDeepCompareState(value || [], []);
const values = useDeepCompareMemoize(value || []);

useCleanupMultiSelectValue({
field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import CalendarIcon from '../icons/Calendar.svg';
import { ENTER_KEYDOWN_EVENT, focusRelevantFlatpickerDay } from '../../util/dateTimeUtil';
import { getLocaleReadableDateFormat, getLocaleDateFlatpickrConfig } from '../../util/localisationUtil';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { useDeepCompareState } from '../../../hooks';
import { useDeepCompareMemoize } from '../../../hooks';

import { InputAdorner } from './InputAdorner';
import { Label } from '../../Label';
Expand Down Expand Up @@ -33,7 +33,7 @@ export function Datepicker(props) {
const [ forceFocusCalendar, setForceFocusCalendar ] = useState(false);

// ensures we render based on date value instead of reference
const date = useDeepCompareState(dateObject, null);
const date = useDeepCompareMemoize(dateObject);

// shorts the date value back to the source
useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/form-js-viewer/src/render/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export { useReadonly } from './useReadonly';
export { useService } from './useService';
export { usePrevious } from './usePrevious';
export { useFlushDebounce } from './useFlushDebounce';
export { useDeepCompareState } from './useDeepCompareState';
export { useDeepCompareMemoize } from './useDeepCompareMemoize';
export { useSingleLineTemplateEvaluation } from './useSingleLineTemplateEvaluation';
export { useTemplateEvaluation } from './useTemplateEvaluation';
export { useCleanupSingleSelectValue } from './useCleanupSingleSelectValue';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect } from 'preact/hooks';
import { LOAD_STATES } from './useOptionsAsync';
import { hasEqualValue } from '../components/util/sanitizerUtil';
import { useDeepCompareState } from './useDeepCompareState';
import { useDeepCompareMemoize } from './useDeepCompareMemoize';

export function useCleanupMultiSelectValue(props) {

Expand All @@ -13,7 +13,7 @@ export function useCleanupMultiSelectValue(props) {
values
} = props;

const memoizedValues = useDeepCompareState(values, []);
const memoizedValues = useDeepCompareMemoize(values || []);

// ensures that the values are always a subset of the possible options
useEffect(() => {
Expand Down
18 changes: 18 additions & 0 deletions packages/form-js-viewer/src/render/hooks/useDeepCompareMemoize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useRef } from 'preact/hooks';
import isEqual from 'lodash/isEqual';

/**
* A custom hook to manage state changes with deep comparison.
*
* @param {any} value - The current value to manage.
* @returns {any} - Returns the current state.
*/
export function useDeepCompareMemoize(value) {
const ref = useRef();

if (!isEqual(value, ref.current)) {
ref.current = value;
}

return ref.current;
}
31 changes: 0 additions & 31 deletions packages/form-js-viewer/src/render/hooks/useDeepCompareState.js

This file was deleted.

36 changes: 15 additions & 21 deletions packages/form-js-viewer/src/render/hooks/useOptionsAsync.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'preact/hooks';
import { useMemo } from 'preact/hooks';
import { normalizeOptionsData } from '../components/util/optionsUtil';
import { useExpressionEvaluation } from './useExpressionEvaluation';
import { useDeepCompareState } from './useDeepCompareState';
import { useDeepCompareMemoize } from './useDeepCompareMemoize';
import { useService } from './useService';

/**
Expand Down Expand Up @@ -32,44 +32,38 @@ export function useOptionsAsync(field) {
values: staticOptions
} = field;

const [ optionsGetter, setOptionsGetter ] = useState({ options: [], error: undefined, loadState: LOAD_STATES.LOADING });
const initialData = useService('form')._getState().initialData;

const expressionEvaluation = useExpressionEvaluation(optionsExpression);
const evaluatedOptions = useDeepCompareState(expressionEvaluation || [], []);

useEffect(() => {
const evaluatedOptions = useDeepCompareMemoize(expressionEvaluation || []);

const optionsGetter = useMemo(() => {
let options = [];

// dynamic options
if (optionsKey !== undefined) {
const keyedOptions = (initialData || {})[ optionsKey ];

const keyedOptions = (initialData || {})[optionsKey];
if (keyedOptions && Array.isArray(keyedOptions)) {
options = keyedOptions;
}
}

// static options
} else if (staticOptions !== undefined) {
else if (staticOptions !== undefined) {
options = Array.isArray(staticOptions) ? staticOptions : [];
}

// expression
} else if (optionsExpression) {
else if (optionsExpression && evaluatedOptions && Array.isArray(evaluatedOptions)) {
options = evaluatedOptions;
}

if (evaluatedOptions && Array.isArray(evaluatedOptions)) {
options = evaluatedOptions;
}
} else {
setOptionsGetter(buildErrorState('No options source defined in the form definition'));
return;
// error case
else {
return buildErrorState('No options source defined in the form definition');
}

// normalize data to support primitives and partially defined objects
options = normalizeOptionsData(options);

setOptionsGetter(buildLoadedState(options));

return buildLoadedState(normalizeOptionsData(options));
}, [ optionsKey, staticOptions, initialData, optionsExpression, evaluatedOptions ]);

return optionsGetter;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import { get } from 'min-dash';
import { SECURITY_ATTRIBUTES_DEFINITIONS, SANDBOX_ATTRIBUTE } from '../../util/constants';
import { useMemo } from 'preact/hooks';
import { useDeepCompareState } from './useDeepCompareState';

/**
* A custom hook to build up security attributes from form configuration.
*
* @param {Object} security - The security configuration.
* @returns {Array} - Returns a tuple with sandbox and allow attributes.
*/
export function useSecurityAttributesMap(security) {

const securityMemoized = useDeepCompareState(security);

const sandbox = useMemo(() =>
SECURITY_ATTRIBUTES_DEFINITIONS
.filter(({ attribute }) => attribute === SANDBOX_ATTRIBUTE)
.filter(({ property }) => get(securityMemoized, [ property ], false))
.map(({ directive }) => directive)
.join(' ')
, [ securityMemoized ]);

const allow = useMemo(() =>
SECURITY_ATTRIBUTES_DEFINITIONS
.filter(({ attribute }) => attribute !== SANDBOX_ATTRIBUTE)
.filter(({ property }) => get(securityMemoized, [ property ], false))
.map(({ directive }) => directive)
.join('; ')
, [ securityMemoized ]);

return [ sandbox, allow ];
}
import { get } from 'min-dash';
import { SECURITY_ATTRIBUTES_DEFINITIONS, SANDBOX_ATTRIBUTE } from '../../util/constants';
import { useMemo } from 'preact/hooks';
import { useDeepCompareMemoize } from './useDeepCompareMemoize';

/**
* A custom hook to build up security attributes from form configuration.
*
* @param {Object} security - The security configuration.
* @returns {Array} - Returns a tuple with sandbox and allow attributes.
*/
export function useSecurityAttributesMap(security) {

const securityMemoized = useDeepCompareMemoize(security);

const sandbox = useMemo(() =>
SECURITY_ATTRIBUTES_DEFINITIONS
.filter(({ attribute }) => attribute === SANDBOX_ATTRIBUTE)
.filter(({ property }) => get(securityMemoized, [ property ], false))
.map(({ directive }) => directive)
.join(' ')
, [ securityMemoized ]);

const allow = useMemo(() =>
SECURITY_ATTRIBUTES_DEFINITIONS
.filter(({ attribute }) => attribute !== SANDBOX_ATTRIBUTE)
.filter(({ property }) => get(securityMemoized, [ property ], false))
.map(({ directive }) => directive)
.join('; ')
, [ securityMemoized ]);

return [ sandbox, allow ];
}

0 comments on commit c6e7a6e

Please sign in to comment.