如何解决在 React Admin 的自定义列表中使用过滤器
我一直在尝试在 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),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;
解决方法
我不确定您要实现的目标。如果想用react-admin过滤本地数据,可以看the useReferenceArrayFieldController source in react-admin。这是如何在本地实现分页、排序和过滤的一个很好的例子。
此逻辑将很快通过 react-admin 3.17 中的独立 useList
钩子提供(请参阅 https://github.com/marmelab/react-admin/pull/6321/files#diff-7948760fe1f4dea6953beaf08888f1a3ce5ef2a359a46778b7c7a38af300ccf3):
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),}));
// 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,})
);
},setFilterValues]
);
const setFilters = useCallback(
(filters,displayedFilters) => {
setFilterValues(removeEmpty(filters));
if (displayedFilters) {
setDisplayedFilters(displayedFilters);
}
setPage(1);
},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,page,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,displayedFilters,hideFilter,ids: finalItems.ids,loaded: loadedState,loading: loadingState,selectedIds,setFilters,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,您应该可以正常运行了。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。