fix(native-filters): loop bug by simplify state handling (#14788)

This commit is contained in:
Ville Brofeldt 2021-05-25 15:03:22 +03:00 committed by GitHub
parent 9bf07cc428
commit 9a96dac3a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 68 deletions

View File

@ -73,6 +73,7 @@ const DefaultValue: FC<DefaultValueProps> = ({
chartType={formFilter?.filterType}
hooks={{ setDataMask }}
enableNoResults={enableNoResults}
filterState={formFilter.defaultDataMask?.filterState}
/>
);
};

View File

@ -29,7 +29,7 @@ import {
} from '@superset-ui/core';
import React, { useCallback, useEffect, useReducer, useState } from 'react';
import { Select } from 'src/common/components';
import { debounce } from 'lodash';
import debounce from 'lodash/debounce';
import { SLOW_DEBOUNCE } from 'src/constants';
import { PluginFilterSelectProps, SelectValue } from './types';
import { StyledSelect, Styles } from '../common';
@ -91,7 +91,6 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
appSection,
} = props;
const {
defaultValue,
enableEmptyFilter,
multiSelect,
showSearch,
@ -100,29 +99,36 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
defaultToFirstItem,
searchAllOptions,
} = formData;
const groupby = ensureIsArray<string>(formData.groupby);
const [col] = groupby;
const [currentSuggestionSearch, setCurrentSuggestionSearch] = useState('');
const [dataMask, dispatchDataMask] = useReducer<DataMaskReducer>(reducer, {
filterState,
ownState: {
coltypeMap,
},
});
const updateDataMask = (values: SelectValue) => {
const emptyFilter =
enableEmptyFilter && !inverseSelection && !values?.length;
dispatchDataMask({
type: 'filterState',
extraFormData: getSelectExtraFormData(
col,
values,
emptyFilter,
inverseSelection,
),
filterState: {
value: values,
},
});
};
const isDisabled =
appSection === AppSection.FILTER_CONFIG_MODAL && defaultToFirstItem;
const groupby = ensureIsArray<string>(formData.groupby);
// Correct initial value for Ant Select
// If we are in config modal we always need show empty select for `defaultToFirstItem`
const [values, setValues] = useState<SelectValue>(
!isDisabled && defaultValue?.length ? defaultValue : [],
);
const [currentSuggestionSearch, setCurrentSuggestionSearch] = useState('');
const [dataMask, dispatchDataMask] = useReducer<DataMaskReducer>(
reducer,
searchAllOptions
? {
ownState: {
coltypeMap,
},
}
: {},
);
const debouncedOwnStateFunc = useCallback(
debounce((val: string) => {
dispatchDataMask({
@ -154,67 +160,46 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
}
};
useEffect(() => {
const firstItem: SelectValue = data[0]
? (groupby.map(col => data[0][col]) as string[])
: null;
if (!isDisabled && defaultToFirstItem && firstItem) {
// initialize to first value if set to default to first item
setValues(firstItem);
} else if (!isDisabled && defaultValue?.length) {
// initialize to saved value
setValues(defaultValue);
}
}, [defaultToFirstItem, defaultValue]);
const handleBlur = () => {
clearSuggestionSearch();
unsetFocusedFilter();
};
const [col] = groupby;
const datatype: GenericDataType = coltypeMap[col];
const labelFormatter = getDataRecordFormatter({
timeFormatter: smartDateDetailedFormatter,
});
const handleChange = (value?: SelectValue | number | string) => {
setValues(ensureIsArray(value));
const values = ensureIsArray(value);
if (values.length === 0) {
updateDataMask(null);
} else {
updateDataMask(values);
}
};
useEffect(() => {
const firstItem: SelectValue = data[0]
? (groupby.map(col => data[0][col]) as string[])
: null;
if (isDisabled) {
setValues([]);
// empty selection if filter is disabled
updateDataMask(null);
} else if (!isDisabled && defaultToFirstItem && firstItem) {
// initialize to first value if set to default to first item
updateDataMask(firstItem);
} else {
// reset data mask based on filter state
updateDataMask(filterState.value);
}
}, [isDisabled]);
useEffect(() => {
const emptyFilter =
enableEmptyFilter && !inverseSelection && values?.length === 0;
dispatchDataMask({
type: 'filterState',
extraFormData: getSelectExtraFormData(
col,
values,
emptyFilter,
inverseSelection,
),
filterState: {
// We need to save in state `FIRST_VALUE` as some const and not as REAL value,
// because when FiltersBar check if all filters initialized it compares `defaultValue` with this value
// and because REAL value can be unpredictable for users that have different data for same dashboard we use `FIRST_VALUE`
value: values,
},
});
}, [col, enableEmptyFilter, inverseSelection, JSON.stringify(values)]);
useEffect(() => {
// handle changes coming from application, e.g. "Clear all" button
if (JSON.stringify(values) !== JSON.stringify(filterState.value)) {
handleChange(filterState.value);
}
}, [JSON.stringify(filterState.value)]);
}, [
col,
isDisabled,
defaultToFirstItem,
enableEmptyFilter,
inverseSelection,
]);
useEffect(() => {
setDataMask(dataMask);
@ -229,7 +214,7 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
<StyledSelect
allowClear={!enableEmptyFilter}
// @ts-ignore
value={values}
value={filterState.value || []}
disabled={isDisabled}
showSearch={showSearch}
mode={multiSelect ? 'multiple' : undefined}
@ -253,7 +238,7 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
);
})}
{currentSuggestionSearch &&
!ensureIsArray(values).some(
!ensureIsArray(filterState.value).some(
suggestion => suggestion === currentSuggestionSearch,
) && (
<Option value={currentSuggestionSearch}>