gpt4 book ai didi

url - 如何不使用 react-admin 更改列表过滤的 url?

转载 作者:行者123 更新时间:2023-12-02 16:47:39 24 4
gpt4 key购买 nike

假设我有一条路线/customers,其中 List 客户 正在呈现。在同一条路线上还有一个 side drawer展示。那个抽屉包含一个带有 Filter 的列表帮助主题

当我开始在侧抽屉的过滤器中输入时,url 发生了变化。这是根据 react-admin 的列表过滤器的工作方式。

问题是客户列表注意到路线变化。实际上,它会根据与帮助主题 相关的搜索词开始查询和重新加载客户。当然没有找到客户。

我希望客户列表不会注意到我正在过滤帮助主题。我的目标解决方案是当我输入帮助主题搜索词时,侧抽屉中的列表过滤器不会更改 url。

我如何配置或自定义侧边抽屉中的过滤器以在键入时不更改 url,而是将当前过滤器值存储在组件状态之类的东西中?

实际上,由于过滤器以一种形式存在(通过 react-final-form),它保持自己的状态,所以我可以接受这样的解决方案。但是当然 publishToUrl 不是 Filter 的可用属性。

const MyFilter = props => (
<Filter {...props} publishToUrl={false} >
<TextInput source="title" />
</Filter>
);

相关:

最佳答案

下面的代码会移除 List 组件与 Redux Store 和 Location 的关联,它可以用来在一个页面上显示多个列表

使用这个列表而不是 react 管理员的列表

创建自己的列表组件

import * as React from 'react';
import PropTypes from 'prop-types';
import {
} from 'ra-core';
import {ListView} from 'ra-ui-materialui';
import {useListController} from '../../controller/useListController';

export const TitlePropType = PropTypes.oneOfType([
PropTypes.string,
PropTypes.element,
]);

/**
* List page component
*
* The <List> component renders the list layout (title, buttons, filters, pagination),
* and fetches the list of records from the REST API.
* It then delegates the rendering of the list of records to its child component.
* Usually, it's a <Datagrid>, responsible for displaying a table with one row for each post.
*
* In Redux terms, <List> is a connected component, and <Datagrid> is a dumb component.
*
* The <List> component accepts the following props:
*
* - actions
* - aside
* - component
* - filter (the permanent filter to apply to the query)
* - filters (a React component used to display the filter form)
* - pagination
* - perPage
* - sort
* - title
*
* @example
*
* const PostFilter = (props) => (
* <Filter {...props}>
* <TextInput label="Search" source="q" alwaysOn />
* <TextInput label="Title" source="title" />
* </Filter>
* );
* export const PostList = (props) => (
* <List {...props}
* title="List of posts"
* sort={{ field: 'published_at' }}
* filter={{ is_published: true }}
* filters={PostFilter}
* >
* <Datagrid>
* <TextField source="id" />
* <TextField source="title" />
* <EditButton />
* </Datagrid>
* </List>
* );
*/
export const List = props => {
return <ListView {...props} {...useListController(props)} />;
}

List.propTypes = {
// the props you can change
actions: PropTypes.element,
aside: PropTypes.element,
bulkActionButtons: PropTypes.oneOfType([PropTypes.element, PropTypes.bool]),
children: PropTypes.node,
classes: PropTypes.object,
className: PropTypes.string,
filter: PropTypes.object,
filterDefaultValues: PropTypes.object,
filters: PropTypes.element,
pagination: PropTypes.element,
perPage: PropTypes.number.isRequired,
sort: PropTypes.shape({
field: PropTypes.string,
order: PropTypes.string,
}),
title: TitlePropType,
// the props managed by react-admin
authProvider: PropTypes.func,
hasCreate: PropTypes.bool.isRequired,
hasEdit: PropTypes.bool.isRequired,
hasList: PropTypes.bool.isRequired,
hasShow: PropTypes.bool.isRequired,
location: PropTypes.object,
match: PropTypes.object,
path: PropTypes.string,
resource: PropTypes.string.isRequired,
};

List.defaultProps = {
filter: {},
perPage: 10,
};

Controller /useListController

import { isValidElement, ReactElement, useEffect, useMemo } from 'react';
import inflection from 'inflection';
import { useSelector } from 'react-redux';
import get from 'lodash/get';

import {useCheckMinimumRequiredProps, useTranslate,
useNotify, useGetList, CRUD_GET_LIST, useVersion, useRecordSelection } from 'react-admin';
import { ListParams } from 'ra-core';
import { Sort, RecordMap, Identifier, ReduxState, Record } from 'ra-core';

import {SORT_ASC} from 'ra-core/esm/reducer/admin/resource/list/queryReducer';
import useListParams from './useListParams';

export interface ListProps {
// the props you can change
filter?: object;
filters?: ReactElement<any>;
filterDefaultValues?: object;
pagination?: ReactElement<any>;
perPage?: number;
sort?: Sort;
// the props managed by react-admin
basePath: string;
debounce?: number;
hasCreate?: boolean;
hasEdit?: boolean;
hasList?: boolean;
hasShow?: boolean;
path?: string;
query: ListParams;
resource: string;
[key: string]: any;
}

const defaultSort = {
field: 'id',
order: SORT_ASC,
};

const defaultData = {};

export interface ListControllerProps<RecordType = Record> {
basePath: string;
currentSort: Sort;
data: RecordMap<RecordType>;
defaultTitle: string;
displayedFilters: any;
filterValues: any;
hasCreate: boolean;
hideFilter: (filterName: string) => void;
ids: Identifier[];
loading: boolean;
loaded: boolean;
onSelect: (ids: Identifier[]) => void;
onToggleItem: (id: Identifier) => void;
onUnselectItems: () => void;
page: number;
perPage: number;
resource: string;
selectedIds: Identifier[];
setFilters: (filters: any, displayedFilters: any) => void;
setPage: (page: number) => void;
setPerPage: (page: number) => void;
setSort: (sort: string) => void;
showFilter: (filterName: string, defaultValue: any) => void;
total: number;
version: number;
}

/**
* Prepare data for the List view
*
* @param {Object} props The props passed to the List component.
*
* @return {Object} controllerProps Fetched and computed data for the List view
*
* @example
*
* import { useListController } from 'react-admin';
* import ListView from './ListView';
*
* const MyList = props => {
* const controllerProps = useListController(props);
* return <ListView {...controllerProps} {...props} />;
* }
*/
export const useListController = <RecordType = Record>(
props: ListProps
): ListControllerProps<RecordType> => {
useCheckMinimumRequiredProps('List', ['basePath', 'resource'], props);

const {
basePath,
resource,
hasCreate,
filterDefaultValues,
sort = defaultSort,
perPage = 10,
filter,
debounce = 500,
} = props;

if (filter && isValidElement(filter)) {
throw new Error(
'<List> received a React element as `filter` props. If you intended to set the list filter elements, use the `filters` (with an s) prop instead. The `filter` prop is internal and should not be set by the developer.'
);
}

const translate = useTranslate();
const notify = useNotify();
const version = useVersion();

const [query, queryModifiers] = useListParams({
resource,
filterDefaultValues,
sort,
perPage,
debounce
});

const [selectedIds, selectionModifiers] = useRecordSelection(resource);

/**
* We want the list of ids to be always available for optimistic rendering,
* and therefore we need a custom action (CRUD_GET_LIST) that will be used.
*/
const { ids, total, loading, loaded } = useGetList<RecordType>(
resource,
{
page: query.page,
perPage: query.perPage,
},
{ field: query.sort, order: query.order },
{ ...query.filter, ...filter },
{
action: CRUD_GET_LIST,
onFailure: error =>
notify(
typeof error === 'string'
? error
: error.message || 'ra.notification.http_error',
'warning'
),
}
);

const data = useSelector(
(state: ReduxState): RecordMap<RecordType> =>
get(
state.admin.resources,
[resource, 'data'],
defaultData
) as RecordMap<RecordType>
);

// When the user changes the page/sort/filter, this controller runs the
// useGetList hook again. While the result of this new call is loading,
// the ids and total are empty. To avoid rendering an empty list at that
// moment, we override the ids and total with the latest loaded ones.
const defaultIds = [];
const defaultTotal = 0;

useEffect(() => {
if (
query.page <= 0 ||
(!loading && query.page > 1 && (ids || []).length === 0)
) {
// query for a page that doesn't exist, set page to 1
queryModifiers.setPage(1);
}
}, [loading, query.page, ids, queryModifiers]);

const currentSort = useMemo(
() => ({
field: query.sort,
order: query.order,
}),
[query.sort, query.order]
);

const resourceName = translate(`resources.${resource}.name`, {
smart_count: 2,
_: inflection.humanize(inflection.pluralize(resource)),
});
const defaultTitle = translate('ra.page.list', {
name: resourceName,
});

return {
basePath,
currentSort,
data,
defaultTitle,
displayedFilters: query.displayedFilters,
filterValues: query.filterValues,
hasCreate,
hideFilter: queryModifiers.hideFilter,
ids: typeof total === 'undefined' ? defaultIds : ids,
loaded: loaded || defaultIds.length > 0,
loading,
onSelect: selectionModifiers.select,
onToggleItem: selectionModifiers.toggle,
onUnselectItems: selectionModifiers.clearSelection,
page: query.page,
perPage: query.perPage,
resource,
selectedIds,
setFilters: queryModifiers.setFilters,
setPage: queryModifiers.setPage,
setPerPage: queryModifiers.setPerPage,
setSort: queryModifiers.setSort,
showFilter: queryModifiers.showFilter,
total: typeof total === 'undefined' ? defaultTotal : total,
version,
};
};

export const injectedProps = [
'basePath',
'currentSort',
'data',
'defaultTitle',
'displayedFilters',
'filterValues',
'hasCreate',
'hideFilter',
'ids',
'loading',
'loaded',
'onSelect',
'onToggleItem',
'onUnselectItems',
'page',
'perPage',
'refresh',
'resource',
'selectedIds',
'setFilters',
'setPage',
'setPerPage',
'setSort',
'showFilter',
'total',
'version',
];

export default useListController;

Controller /useListParams

import {useCallback, useMemo, useState} from 'react';
import lodashDebounce from 'lodash/debounce';
import set from 'lodash/set';
import { ListParams } from 'ra-core';
import { Sort, removeKey, removeEmpty } from 'ra-core';
import queryReducer from 'ra-core/esm/reducer/admin/resource/list/queryReducer';
import {SORT_ASC, SET_SORT, SET_PAGE, SET_PER_PAGE, SET_FILTER} from 'ra-core/esm/reducer/admin/resource/list/queryReducer';

interface ListParamsOptions {
resource: string;
perPage?: number;
sort?: Sort;
filterDefaultValues?: object;
debounce?: number;
}

interface Parameters extends ListParams {
filterValues: object;
displayedFilters: {
[key: string]: boolean;
};
requestSignature: any[];
}

interface Modifiers {
changeParams: (action: any) => void;
setPage: (page: number) => void;
setPerPage: (pageSize: number) => void;
setSort: (sort: string) => void;
setFilters: (filters: any, displayedFilters: any) => void;
hideFilter: (filterName: string) => void;
showFilter: (filterName: string, defaultValue: any) => void;
}

const emptyObject = {};

const defaultSort = {
field: 'id',
order: SORT_ASC,
};

const defaultParams = {};

/**
* Get the list parameters (page, sort, filters) and modifiers.
*
* These parameters are merged from 3 sources:
* - the query string from the URL
* - the params stored in the state (from previous navigation)
* - the options passed to the hook (including the filter defaultValues)
*
* @returns {Array} A tuple [parameters, modifiers].
* Destructure as [
* { page, perPage, sort, order, filter, filterValues, displayedFilters, requestSignature },
* { setFilters, hideFilter, showFilter, setPage, setPerPage, setSort }
* ]
*
* @example
*
* const [listParams, listParamsActions] = useListParams({
* resource: 'posts',
* location: location // From react-router. Injected to your component by react-admin inside a List
* filterDefaultValues: {
* published: true
* },
* sort: {
* field: 'published_at',
* order: 'DESC'
* },
* perPage: 25
* });
*
* const {
* page,
* perPage,
* sort,
* order,
* filter,
* filterValues,
* displayedFilters,
* requestSignature
* } = listParams;
*
* const {
* setFilters,
* hideFilter,
* showFilter,
* setPage,
* setPerPage,
* setSort,
* } = listParamsActions;
*/
const useListParams = ({
resource,
filterDefaultValues,
sort = defaultSort,
perPage = 10,
debounce = 500,
}: ListParamsOptions): [Parameters, Modifiers] => {

const [params, setParams] = useState(defaultParams);

const requestSignature = [
resource,
params,
filterDefaultValues,
JSON.stringify(sort),
perPage
];

const query = useMemo(
() =>
getQuery({
params,
filterDefaultValues,
sort,
perPage
}),
requestSignature // eslint-disable-line react-hooks/exhaustive-deps
);

const changeParams = useCallback(action => {
const newParams = queryReducer(query, action);
setParams(newParams);
}, requestSignature); // eslint-disable-line react-hooks/exhaustive-deps

const setSort = useCallback(
(newSort: string) =>
changeParams({ type: SET_SORT, payload: { sort: newSort } }),
requestSignature // eslint-disable-line react-hooks/exhaustive-deps
);

const setPage = useCallback(
(newPage: number) => changeParams({ type: SET_PAGE, payload: newPage }),
requestSignature // eslint-disable-line react-hooks/exhaustive-deps
);

const setPerPage = useCallback(
(newPerPage: number) =>
changeParams({ type: SET_PER_PAGE, payload: newPerPage }),
requestSignature // eslint-disable-line react-hooks/exhaustive-deps
);

const filterValues = query.filter || emptyObject;
const displayedFilterValues = query.displayedFilters || emptyObject;

const debouncedSetFilters = lodashDebounce(
(newFilters, newDisplayedFilters) => {
let payload = {
filter: removeEmpty(newFilters),
displayedFilters: undefined,
};
if (newDisplayedFilters) {
payload.displayedFilters = Object.keys(
newDisplayedFilters
).reduce((filters, filter) => {
return newDisplayedFilters[filter]
? { ...filters, [filter]: true }
: filters;
}, {});
}
changeParams({
type: SET_FILTER,
payload,
});
},
debounce
);

const setFilters = useCallback(
(filters, displayedFilters) =>
debouncedSetFilters(filters, displayedFilters),
requestSignature // eslint-disable-line react-hooks/exhaustive-deps
);

const hideFilter = useCallback((filterName: string) => {
const newFilters = removeKey(filterValues, filterName);
const newDisplayedFilters = {
...displayedFilterValues,
[filterName]: undefined,
};

setFilters(newFilters, newDisplayedFilters);
}, requestSignature); // eslint-disable-line react-hooks/exhaustive-deps

const showFilter = useCallback((filterName: string, defaultValue: any) => {
const newFilters = set(filterValues, filterName, defaultValue);
const newDisplayedFilters = {
...displayedFilterValues,
[filterName]: true,
};
setFilters(newFilters, newDisplayedFilters);
}, requestSignature); // eslint-disable-line react-hooks/exhaustive-deps

return [
{
displayedFilters: displayedFilterValues,
filterValues,
requestSignature,
...query,
},
{
changeParams,
setPage,
setPerPage,
setSort,
setFilters,
hideFilter,
showFilter,
},
];
};

/**
* Check if user has already set custom sort, page, or filters for this list
*
* User params come from the Redux store as the params props. By default,
* this object is:
*
* { filter: {}, order: null, page: 1, perPage: null, sort: null }
*
* To check if the user has custom params, we must compare the params
* to these initial values.
*
* @param {Object} params
*/
export const hasCustomParams = (params: ListParams) => {
return (
params &&
params.filter &&
(Object.keys(params.filter).length > 0 ||
params.order != null ||
params.page !== 1 ||
params.perPage != null ||
params.sort != null)
);
};

/**
* Merge list params from 3 different sources:
* - the query string
* - the params stored in the state (from previous navigation)
* - the props passed to the List component (including the filter defaultValues)
*/
export const getQuery = ({
filterDefaultValues,
params,
sort,
perPage,
}) => {
const query: Partial<ListParams> =
hasCustomParams(params)
? { ...params } : { filter: filterDefaultValues || {} };

if (!query.sort) {
query.sort = sort.field;
query.order = sort.order;
}
if (!query.perPage) {
query.perPage = perPage;
}
if (!query.page) {
query.page = 1;
}
return {
...query,
page: getNumberOrDefault(query.page, 1),
perPage: getNumberOrDefault(query.perPage, 10),
} as ListParams;
};

export const getNumberOrDefault = (
possibleNumber: string | number | undefined,
defaultValue: number
) =>
(typeof possibleNumber === 'string'
? parseInt(possibleNumber, 10)
: possibleNumber) || defaultValue;

export default useListParams;

关于url - 如何不使用 react-admin 更改列表过滤的 url?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59883643/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com