Skip to content

Commit

Permalink
add useReferenceSearch hook
Browse files Browse the repository at this point in the history
  • Loading branch information
ThieryMichel committed Jun 13, 2019
1 parent fe9342a commit 822e9a9
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 96 deletions.
114 changes: 22 additions & 92 deletions packages/ra-core/src/controller/input/ReferenceInputController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
Expand All @@ -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;
Expand Down Expand Up @@ -142,25 +130,33 @@ export const ReferenceInputController: FunctionComponent<Props> = ({
onChange,
children,
perPage,
filter: initialFilter,
filter: permanentFilter,
reference,
filterToQuery,
referenceSource = defaultReferenceSource,
resource,
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,
Expand All @@ -175,38 +171,6 @@ export const ReferenceInputController: FunctionComponent<Props> = ({
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,
Expand All @@ -222,38 +186,4 @@ export const ReferenceInputController: FunctionComponent<Props> = ({
}) 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<Props>;
125 changes: 125 additions & 0 deletions packages/ra-core/src/controller/input/useReferenceSearch.ts
Original file line number Diff line number Diff line change
@@ -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);
8 changes: 4 additions & 4 deletions packages/ra-core/src/controller/useFilterState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface Filter {

interface UseFilterStateOptions {
filterToQuery: (v: string) => Filter;
initialFilter: Filter;
permanentFilter: Filter;
debounceTime?: number;
}

Expand All @@ -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
Expand Down

0 comments on commit 822e9a9

Please sign in to comment.