diff --git a/web/client/plugins/featuregrid/FeatureEditor.jsx b/web/client/plugins/featuregrid/FeatureEditor.jsx index 935efab545..5367d50239 100644 --- a/web/client/plugins/featuregrid/FeatureEditor.jsx +++ b/web/client/plugins/featuregrid/FeatureEditor.jsx @@ -28,6 +28,15 @@ import {gridTools, gridEvents, pageEvents, toolbarEvents} from './index'; const EMPTY_ARR = []; const EMPTY_OBJ = {}; +/** + * Custom check for filterRenderers useMemo function + * @param {object} prevProps previous props + * @param {object} nextProps next props + * @returns {boolean} + */ +export const checkFilterRendererProps = (prevProps, nextProps) => { + return isEqual(prevProps.describe, nextProps.describe) && isEqual(prevProps.fields, nextProps.fields); +}; const Dock = connect(createSelector( getDockSize, @@ -187,7 +196,9 @@ const FeatureDock = (props = { }; const items = props?.items ?? []; const toolbarItems = items.filter(({target}) => target === 'toolbar'); - const filterRenderers = useMemo(() => getFilterRenderers(props.describe, props.fields), [props.describe, props.fields]); + // ensure to avoid re-rendering of the feature grid (lost focus on every render) in any case if for some + // reason the describeFeatureType or fields are generated but equal. + const filterRenderers = useMemo(() => getFilterRenderers(props.describe, props.fields), checkFilterRendererProps); return (
{ props.onSizeChange(size, dockProps); }}> diff --git a/web/client/plugins/featuregrid/__tests__/FeatureEditor-test.jsx b/web/client/plugins/featuregrid/__tests__/FeatureEditor-test.jsx index d8e399fdb7..44b56b4534 100644 --- a/web/client/plugins/featuregrid/__tests__/FeatureEditor-test.jsx +++ b/web/client/plugins/featuregrid/__tests__/FeatureEditor-test.jsx @@ -1,6 +1,6 @@ import expect from 'expect'; -import {selector } from '../FeatureEditor'; +import {selector, checkFilterRendererProps } from '../FeatureEditor'; describe('FeatureEditor plugin component', () => { @@ -104,6 +104,25 @@ describe('FeatureEditor plugin component', () => { expect(result.fields).toEqual(FIELDS); }); }); + describe('utility functions', () => { + it('checkFilterRendererProps', () => { + // check if the function returns true if the props are the same + expect(checkFilterRendererProps({ + describe: {}, + fields: [] + }, { + describe: {}, + fields: [] + })).toBe(true); + expect(checkFilterRendererProps({ + describe: {}, + fields: [] + }, { + describe: {}, + fields: [{name: 'name', type: 'string'}] + })).toBe(false); + }); + }); }); diff --git a/web/client/selectors/__tests__/featuregrid-test.js b/web/client/selectors/__tests__/featuregrid-test.js index 471e6e25cb..b983659525 100644 --- a/web/client/selectors/__tests__/featuregrid-test.js +++ b/web/client/selectors/__tests__/featuregrid-test.js @@ -671,6 +671,20 @@ describe('Test featuregrid selectors', () => { } }; expect(selectedLayerFieldsSelector(state)).toEqual([FIELD]); + // check that fields are memoized when applying defaults + const stateEmptyFields = { + featuregrid: { + selectedLayer: 'TEST_LAYER' + }, + layers: { + flat: [{ + id: "TEST_LAYER", + title: "Test Layer", + name: 'editing:polygons.test' + }] + } + }; + expect(selectedLayerFieldsSelector(stateEmptyFields)).toBe(selectedLayerFieldsSelector(stateEmptyFields)); }); it('editingAllowedGroupsSelector', () => { const editingAllowedGroups = ['test']; diff --git a/web/client/selectors/featuregrid.js b/web/client/selectors/featuregrid.js index b9856e6de8..e7f6fc3ac5 100644 --- a/web/client/selectors/featuregrid.js +++ b/web/client/selectors/featuregrid.js @@ -112,7 +112,7 @@ export const getTitleSelector = state => { const title = getTitle(getLayerById(state, selectedLayerIdSelector(state))); return isObject(title) ? title[currentLocaleSelector(state)] || title.default || '' : title; }; - +const STANDARD_FIELDS = []; /** * Returns the current selected layer (featuregrid) fields, if any. * @param {*} state @@ -120,7 +120,7 @@ export const getTitleSelector = state => { */ export const selectedLayerFieldsSelector = state => { const layer = getLayerById(state, selectedLayerIdSelector(state)); - const fields = get(layer, "fields", []); + const fields = get(layer, "fields", STANDARD_FIELDS); return fields; }; export const getCustomizedAttributes = state => {