我一直在尝试在react admin中实现一个过滤功能,对于一个没有任何问题的常规列表,我已经在多个列表中实现了这个功能。我使用了这些docs,它非常简单。
这里的问题是,我使用的是ListContextProvider,而且我一直无法找到如何使用它来实现过滤器。尝试使用上面提供的文档实现不会产生任何结果。有谁能给我指个方向吗?
import React, { useState } from 'react'
import {
Datagrid,
TextField,
ReferenceField,
FunctionField,
Pagination,
Loading,
ListContextProvider,
useQuery,
} from 'react-admin';
import keyBy from 'lodash/keyBy'
import Button from '@material-ui/core/Button';
import "../../css/styles.css";
function CustomList(props) {
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(50);
const [sort, setSort] = useState({ field: 'id', order: 'DESC' })
const { data, total, loading, error } = useQuery({
type: 'getList',
resource: 'list',
payload: {
pagination: { page, perPage },
sort,
filter: { id: props.id },
}
});
if (loading) {
return <Loading />
}
if (error) {
window.location.reload(false);
}
return (
<div>
<ListContextProvider value={{
data: keyBy(data, 'id'),
ids: data.map(({ id }) => id),
total,
page,
perPage,
setPage,
setPerPage,
setSort: (field, order) => {
setSort({ field, order });
},
currentSort: sort,
basePath: "/list",
resource: 'lists',
selectedIds: []
}}>
<Datagrid style={{ tableLayout: 'fixed', wordWrap: "break-word" }}>
<TextField source="id" />
<TextField source="name" />
</Datagrid>
<Pagination rowsPerPageOptions={[10, 25, 50, 100]} />
</ListContextProvider>
</div>
);
}
export default CustomList;
发布于 2021-07-07 02:30:10
我不确定你想要达到什么目的。如果你想用react-admin过滤本地数据,你可以看看the useReferenceArrayFieldController source in react-admin。这是一个很好的例子,说明了如何在本地实现分页、排序和过滤。
这个逻辑很快就会通过react-Admin3.17(参见https://github.com/marmelab/react-admin/pull/6321/files#diff-7948760fe1f4dea6953beaf08888f1a3ce5ef2a359a46778b7c7a38af300ccf3)中的一个独立的useList
钩子提供:
import { useCallback, useEffect, useRef } from 'react';
import get from 'lodash/get';
import isEqual from 'lodash/isEqual';
import { indexById, removeEmpty, useSafeSetState } from '../util';
import {
FilterPayload,
Identifier,
Record,
RecordMap,
SortPayload,
} from '../types';
import usePaginationState from './usePaginationState';
import useSortState from './useSortState';
import useSelectionState from './useSelectionState';
import { ListControllerProps } from '.';
/**
* Handle filtering, sorting and pagination on local data.
*
* Returns the data and callbacks expected by <ListContext>.
*
* @example
* const data = [
* { id: 1, name: 'Arnold' },
* { id: 2, name: 'Sylvester' },
* { id: 3, name: 'Jean-Claude' },
* ]
* const ids = [1, 2, 3];
*
* const MyComponent = () => {
* const listContext = useList({
* initialData: data,
* initialIds: ids,
* basePath: '/resource';
* resource: 'resource';
* });
* return (
* <ListContextProvider value={listContext}>
* <Datagrid>
* <TextField source="id" />
* <TextField source="name" />
* </Datagrid>
* </ListContextProvider>
* );
* };
*
* @param {UseListOptions} props
* @param {Record[]} props.data An array of records
* @param {Identifier[]} props.ids An array of the record identifiers
* @param {Boolean} props.loaded: A boolean indicating whether the data has been loaded at least once
* @param {Boolean} props.loading: A boolean indicating whether the data is being loaded
* @param {Error | String} props.error: Optional. The error if any occured while loading the data
* @param {Object} props.filter: Optional. An object containing the filters applied on the data
* @param {Number} props.page: Optional. The initial page index
* @param {Number} props.perPage: Optional. The initial page size
* @param {SortPayload} props.sort: Optional. The initial sort (field and order)
*/
export const useList = (props: UseListOptions): UseListValue => {
const {
data,
error,
filter = defaultFilter,
ids,
loaded,
loading,
page: initialPage = 1,
perPage: initialPerPage = 1000,
sort: initialSort = defaultSort,
} = props;
const [loadingState, setLoadingState] = useSafeSetState<boolean>(loading);
const [loadedState, setLoadedState] = useSafeSetState<boolean>(loaded);
const [finalItems, setFinalItems] = useSafeSetState<{
data: RecordMap;
ids: Identifier[];
}>(() => ({
data: indexById(data),
ids,
}));
// pagination logic
const { page, setPage, perPage, setPerPage } = usePaginationState({
page: initialPage,
perPage: initialPerPage,
});
// sort logic
const { sort, setSort: setSortObject } = useSortState(initialSort);
const setSort = useCallback(
(field: string, order = 'ASC') => {
setSortObject({ field, order });
setPage(1);
},
[setPage, setSortObject]
);
// selection logic
const {
selectedIds,
onSelect,
onToggleItem,
onUnselectItems,
} = useSelectionState();
// filter logic
const filterRef = useRef(filter);
const [displayedFilters, setDisplayedFilters] = useSafeSetState<{
[key: string]: boolean;
}>({});
const [filterValues, setFilterValues] = useSafeSetState<{
[key: string]: any;
}>(filter);
const hideFilter = useCallback(
(filterName: string) => {
setDisplayedFilters(previousState => {
const { [filterName]: _, ...newState } = previousState;
return newState;
});
setFilterValues(previousState => {
const { [filterName]: _, ...newState } = previousState;
return newState;
});
},
[setDisplayedFilters, setFilterValues]
);
const showFilter = useCallback(
(filterName: string, defaultValue: any) => {
setDisplayedFilters(previousState => ({
...previousState,
[filterName]: true,
}));
setFilterValues(previousState =>
removeEmpty({
...previousState,
[filterName]: defaultValue,
})
);
},
[setDisplayedFilters, setFilterValues]
);
const setFilters = useCallback(
(filters, displayedFilters) => {
setFilterValues(removeEmpty(filters));
if (displayedFilters) {
setDisplayedFilters(displayedFilters);
}
setPage(1);
},
[setDisplayedFilters, setFilterValues, setPage]
);
// handle filter prop change
useEffect(() => {
if (!isEqual(filter, filterRef.current)) {
filterRef.current = filter;
setFilterValues(filter);
}
});
// We do all the data processing (filtering, sorting, paginating) client-side
useEffect(() => {
if (!loaded) return;
// 1. filter
let tempData = data.filter(record =>
Object.entries(filterValues).every(([filterName, filterValue]) => {
const recordValue = get(record, filterName);
const result = Array.isArray(recordValue)
? Array.isArray(filterValue)
? recordValue.some(item => filterValue.includes(item))
: recordValue.includes(filterValue)
: Array.isArray(filterValue)
? filterValue.includes(recordValue)
: filterValue == recordValue; // eslint-disable-line eqeqeq
return result;
})
);
// 2. sort
if (sort.field) {
tempData = tempData.sort((a, b) => {
if (get(a, sort.field) > get(b, sort.field)) {
return sort.order === 'ASC' ? 1 : -1;
}
if (get(a, sort.field) < get(b, sort.field)) {
return sort.order === 'ASC' ? -1 : 1;
}
return 0;
});
}
// 3. paginate
tempData = tempData.slice((page - 1) * perPage, page * perPage);
const finalData = indexById(tempData);
const finalIds = tempData
.filter(data => typeof data !== 'undefined')
.map(data => data.id);
setFinalItems({
data: finalData,
ids: finalIds,
});
}, [
data,
filterValues,
loaded,
page,
perPage,
setFinalItems,
sort.field,
sort.order,
]);
useEffect(() => {
if (loaded !== loadedState) {
setLoadedState(loaded);
}
}, [loaded, loadedState, setLoadedState]);
useEffect(() => {
if (loading !== loadingState) {
setLoadingState(loading);
}
}, [loading, loadingState, setLoadingState]);
return {
currentSort: sort,
data: finalItems.data,
error,
displayedFilters,
filterValues,
hideFilter,
ids: finalItems.ids,
loaded: loadedState,
loading: loadingState,
onSelect,
onToggleItem,
onUnselectItems,
page,
perPage,
selectedIds,
setFilters,
setPage,
setPerPage,
setSort,
showFilter,
total: finalItems.ids.length,
};
};
export interface UseListOptions<RecordType extends Record = Record> {
data: RecordType[];
ids: Identifier[];
error?: any;
filter?: FilterPayload;
loading: boolean;
loaded: boolean;
page?: number;
perPage?: number;
sort?: SortPayload;
}
export type UseListValue = Omit<
ListControllerProps,
'resource' | 'basePath' | 'refetch'
>;
const defaultFilter = {};
const defaultSort = { field: null, order: null };
将该useList hoot的返回值传递给ListContextProvider,您就应该可以正常运行了。
https://stackoverflow.com/questions/68274953
复制相似问题