首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在React Admin的自定义列表中使用过滤器

在React Admin的自定义列表中使用过滤器
EN

Stack Overflow用户
提问于 2021-07-07 01:23:46
回答 1查看 730关注 0票数 1

我一直在尝试在react admin中实现一个过滤功能,对于一个没有任何问题的常规列表,我已经在多个列表中实现了这个功能。我使用了这些docs,它非常简单。

这里的问题是,我使用的是ListContextProvider,而且我一直无法找到如何使用它来实现过滤器。尝试使用上面提供的文档实现不会产生任何结果。有谁能给我指个方向吗?

代码语言:javascript
运行
复制
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; 
EN

回答 1

Stack Overflow用户

发布于 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钩子提供:

代码语言:javascript
运行
复制
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,您就应该可以正常运行了。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68274953

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档