From 822e9a9e27b56124b19c27f06aa74d9fae16d002 Mon Sep 17 00:00:00 2001 From: thiery Date: Thu, 13 Jun 2019 15:53:59 +0200 Subject: [PATCH] add useReferenceSearch hook --- .../input/ReferenceInputController.tsx | 114 +++------------- .../controller/input/useReferenceSearch.ts | 125 ++++++++++++++++++ .../ra-core/src/controller/useFilterState.ts | 8 +- 3 files changed, 151 insertions(+), 96 deletions(-) create mode 100644 packages/ra-core/src/controller/input/useReferenceSearch.ts diff --git a/packages/ra-core/src/controller/input/ReferenceInputController.tsx b/packages/ra-core/src/controller/input/ReferenceInputController.tsx index 3142921ecc9..67d50726c5f 100644 --- a/packages/ra-core/src/controller/input/ReferenceInputController.tsx +++ b/packages/ra-core/src/controller/input/ReferenceInputController.tsx @@ -3,26 +3,14 @@ import { ComponentType, FunctionComponent, ReactElement, - useEffect, } from 'react'; -// @ts-ignore -import { useSelector, useDispatch } from 'react-redux'; -import { createSelector } from 'reselect'; import { WrappedFieldInputProps } from 'redux-form'; -import { crudGetMatchingAccumulate } from '../../actions/accumulateActions'; -import { - getPossibleReferences, - getPossibleReferenceValues, - getReferenceResource, -} from '../../reducer'; import { getStatusForInput as getDataStatus } from './referenceDataStatus'; import useTranslate from '../../i18n/useTranslate'; import { Sort, Record, Pagination } from '../../types'; -import usePaginationState from '../usePaginationState'; -import useSortState from '../useSortState'; -import useFilterState, { Filter } from '../useFilterState'; import useReference from '../useReference'; +import useReferenceSearch from './useReferenceSearch'; const defaultReferenceSource = (resource: string, source: string) => `${resource}@${source}`; @@ -45,7 +33,7 @@ interface Props { allowEmpty?: boolean; basePath: string; children: (params: ChildrenFuncParams) => ReactNode; - filter?: Filter; + filter?: any; filterToQuery: (filter: string) => any; input?: WrappedFieldInputProps; perPage: number; @@ -142,7 +130,7 @@ export const ReferenceInputController: FunctionComponent = ({ onChange, children, perPage, - filter: initialFilter, + filter: permanentFilter, reference, filterToQuery, referenceSource = defaultReferenceSource, @@ -150,17 +138,25 @@ export const ReferenceInputController: FunctionComponent = ({ source, }) => { const translate = useTranslate(); - const dispatch = useDispatch(); - const matchingReferences = useSelector( - getMatchingReferences({ - referenceSource, - input, - reference, - resource, - source, - }), - [input.value, referenceSource, reference, source, resource] - ); + + const { + matchingReferences, + filter, + setFilter, + pagination, + setPagination, + sort, + setSort, + } = useReferenceSearch({ + reference, + referenceSource, + filterToQuery, + filterValue: input.value, + permanentFilter, + perPage, + resource, + source, + }); const { referenceRecord } = useReference({ id: input.value, @@ -175,38 +171,6 @@ export const ReferenceInputController: FunctionComponent = ({ translate, }); - const { pagination, setPagination } = usePaginationState(perPage); - const { sort, setSort } = useSortState(); - const { filter, setFilter } = useFilterState({ - initialFilter, - filterToQuery, - }); - - useEffect( - () => - fetchOptions({ - dispatch, - filter, - reference, - referenceSource, - resource, - source, - pagination, - sort, - }), - [ - filter, - reference, - referenceSource, - resource, - source, - pagination.page, - pagination.perPage, - sort.field, - sort.order, - ] - ); - return children({ choices: dataStatus.choices, error: dataStatus.error, @@ -222,38 +186,4 @@ export const ReferenceInputController: FunctionComponent = ({ }) as ReactElement; }; -const fetchOptions = ({ - dispatch, - filter, - reference, - referenceSource, - resource, - source, - pagination, - sort, -}) => { - dispatch( - crudGetMatchingAccumulate( - reference, - referenceSource(resource, source), - pagination, - sort, - filter - ) - ); -}; - -const matchingReferencesSelector = createSelector( - [ - getReferenceResource, - getPossibleReferenceValues, - (_, props) => props.input.value, - ], - (referenceState, possibleValues, inputId) => - getPossibleReferences(referenceState, possibleValues, [inputId]) -); - -const getMatchingReferences = props => state => - matchingReferencesSelector(state, props); - export default ReferenceInputController as ComponentType; diff --git a/packages/ra-core/src/controller/input/useReferenceSearch.ts b/packages/ra-core/src/controller/input/useReferenceSearch.ts new file mode 100644 index 00000000000..e2aae65b2f7 --- /dev/null +++ b/packages/ra-core/src/controller/input/useReferenceSearch.ts @@ -0,0 +1,125 @@ +import { useEffect } from 'react'; +// @ts-ignore +import { useSelector, useDispatch } from 'react-redux'; +import { createSelector } from 'reselect'; + +import usePaginationState from '../usePaginationState'; +import useSortState from '../useSortState'; +import useFilterState, { Filter } from '../useFilterState'; +import { crudGetMatchingAccumulate } from '../../actions/accumulateActions'; +import { + getPossibleReferences, + getPossibleReferenceValues, + getReferenceResource, +} from '../../reducer'; + +interface UseReferenceSearchOption { + reference: string; + referenceSource: (resource: string, source: string) => string; + resource: string; + source: string; + permanentFilter: Filter; + filterToQuery: (v: string) => Filter; + perPage: number; + filterValue: string; +} + +export default ({ + reference, + referenceSource, + resource, + source, + permanentFilter, + filterToQuery, + perPage, + filterValue, +}: UseReferenceSearchOption) => { + const dispatch = useDispatch(); + + const { pagination, setPagination } = usePaginationState(perPage); + const { sort, setSort } = useSortState(); + const { filter, setFilter } = useFilterState({ + permanentFilter, + filterToQuery, + }); + + useEffect( + () => + fetchOptions({ + dispatch, + filter, + reference, + referenceSource, + resource, + source, + pagination, + sort, + }), + [ + filter, + reference, + referenceSource, + resource, + source, + pagination.page, + pagination.perPage, + sort.field, + sort.order, + ] + ); + + const matchingReferences = useSelector( + getMatchingReferences({ + referenceSource, + filterValue, + reference, + resource, + source, + }), + [filterValue, referenceSource, reference, source, resource] + ); + + return { + matchingReferences, + setFilter, + filter, + pagination, + setPagination, + sort, + setSort, + }; +}; + +const fetchOptions = ({ + dispatch, + filter, + reference, + referenceSource, + resource, + source, + pagination, + sort, +}) => { + dispatch( + crudGetMatchingAccumulate( + reference, + referenceSource(resource, source), + pagination, + sort, + filter + ) + ); +}; + +const matchingReferencesSelector = createSelector( + [ + getReferenceResource, + getPossibleReferenceValues, + (_, props) => props.filterValue, + ], + (referenceState, possibleValues, inputId) => + getPossibleReferences(referenceState, possibleValues, [inputId]) +); + +const getMatchingReferences = props => state => + matchingReferencesSelector(state, props); diff --git a/packages/ra-core/src/controller/useFilterState.ts b/packages/ra-core/src/controller/useFilterState.ts index f814aed33fe..984028a020c 100644 --- a/packages/ra-core/src/controller/useFilterState.ts +++ b/packages/ra-core/src/controller/useFilterState.ts @@ -7,7 +7,7 @@ export interface Filter { interface UseFilterStateOptions { filterToQuery: (v: string) => Filter; - initialFilter: Filter; + permanentFilter: Filter; debounceTime?: number; } @@ -18,15 +18,15 @@ interface UseFilterStateProps { export default ({ filterToQuery = v => ({ q: v }), - initialFilter, + permanentFilter, debounceTime = 500, }: UseFilterStateOptions): UseFilterStateProps => { - const [filter, setFilterValue] = useState(initialFilter); + const [filter, setFilterValue] = useState(permanentFilter); const setFilter = debounce( value => setFilterValue({ - ...initialFilter, + ...permanentFilter, ...filterToQuery(value), }), debounceTime