Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove list's setFilters default debounce #9682

Merged
merged 10 commits into from
Feb 27, 2024
24 changes: 2 additions & 22 deletions docs/FilteringTutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,14 +543,14 @@ const PostFilterForm = () => {

const onSubmit = (values) => {
if (Object.keys(values).length > 0) {
setFilters(values, undefined, false);
setFilters(values);
} else {
hideFilter("main");
}
};

const resetFilter = () => {
setFilters({}, [], false);
setFilters({}, []);
};

return (
Expand Down Expand Up @@ -626,23 +626,3 @@ export const PostList = () => (
**Tip**: No need to pass any `filters` to the list anymore, as the `<PostFilterForm>` component will display them.

You can use a similar approach to offer alternative User Experiences for data filtering, e.g. to display the filters as a line in the datagrid headers.

## Using `setFilters` With Other List Context Methods

The `setFilters` method takes three arguments:

- `filters`: an object containing the new filter values
- `displayedFilters`: an object containing the new displayed filters
- `debounced`: set to false to disable the debounce (true by default)

So `setFilters` is debounced by default, to avoid making too many requests to the server when using search-as-you-type inputs.

When you use `setFilters` and other list context methods (like `setSort`) in a single function, you must disable the debounce. Otherwise, the `setFilters` call would override the other changes.

```jsx
const changeListParams = () => {
setSort({ field: 'name', order: 'ASC' });
// The 3rd parameter disables the debounce ⤵
setFilters({ is_published: true }, undefined, false);
};
```
24 changes: 24 additions & 0 deletions docs/Upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,30 @@ const PostEdit = () => (
);
```

## `setFilters` Is No Longer Debounced By Default

If you're using the `useListContext` hook to filter a list, you might have used the `setFilters` function to update the filters. In react-admin v5, the `setFilters` function is no longer debounced by default. If you want to debounce the filters, you'll have to pass `true` as the third argument:

```diff
import { useListContext } from 'react-admin';

const MyFilter = () => {
const { filterValues, setFilters } = useListContext();
const handleChange = (event) => {
- setFilters({ ...filterValues, [event.target.name]: event.target.value });
+ setFilters({ ...filterValues, [event.target.name]: event.target.value }, undefined, true);
};

return (
<form>
<input name="country" value={filterValues.country} onChange={handleChange} />
<input name="city" value={filterValues.city} onChange={handleChange} />
<input name="zipcode" value={filterValues.zipcode} onChange={handleChange} />
</form>
);
};
```

## Fields Components Requires The `source` Prop

The `FieldProps` interface now requires the `source` prop to be defined. As a consequence, all the default fields components also require the `source` prop to be defined.
Expand Down
17 changes: 2 additions & 15 deletions docs/useListContext.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ The `setFilters` method is used to update the filters. It takes three arguments:

- `filters`: an object containing the new filter values
- `displayedFilters`: an object containing the new displayed filters
- `debounced`: set to false to disable the debounce (true by default)
- `debounced`: set to true to debounce the call to setFilters (false by default)

You can use it to update the filters in a custom filter component:

Expand All @@ -142,8 +142,7 @@ const CustomFilter = () => {

const handleSubmit = (event) => {
event.preventDefault();
// The 3rd parameter disables the debounce ⤵
setFilters(filterFormValues, undefined, false);
setFilters(filterFormValues);
};

return (
Expand All @@ -157,18 +156,6 @@ const CustomFilter = () => {
};
```

Beware that `setFilters` is debounced by default, to avoid making too many requests to the server when using search-as-you-type inputs. In the example above, this is not necessary. That's why you should set the third argument to `setFilters` is set to `false` to disable the debounce.

Disabling the debounce with the third parameter is also necessary when you use `setFilters` and other list context methods (like `setSort`) in a single function. Otherwise, the `setFilters` call would override the other changes.

```jsx
const changeListParams = () => {
setSort({ field: 'name', order: 'ASC' });
// The 3rd parameter disables the debounce ⤵
setFilters({ is_published: true }, undefined, false);
};
```

## TypeScript

The `useListContext` hook accepts a generic parameter for the record type:
Expand Down
17 changes: 2 additions & 15 deletions docs/useListController.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ The `setFilters` method is used to update the filters. It takes three arguments:

- `filters`: an object containing the new filter values
- `displayedFilters`: an object containing the new displayed filters
- `debounced`: set to false to disable the debounce (true by default)
- `debounced`: set to true to debounce the call to setFilters (false by default)

You can use it to update the list filters:

Expand All @@ -282,8 +282,7 @@ const OfficeList = () => {

const handleSubmit = (event) => {
event.preventDefault();
// The 3rd parameter disables the debounce ⤵
setFilters(filterFormValues, undefined, false);
setFilters(filterFormValues);
};

if (isLoading) return <div>Loading...</div>;
Expand All @@ -305,15 +304,3 @@ const OfficeList = () => {
);
};
```

Beware that `setFilters` is debounced by default, to avoid making too many requests to the server when using search-as-you-type inputs. In the example above, this is not necessary. That's why you should set the third argument to `setFilters` is set to `false` to disable the debounce.

Disabling the debounce with the third parameter is also necessary when you use `setFilters` and other list controller methods (like `setSort`) in a single function. Otherwise, the `setFilters` call would override the other changes.

```jsx
const changeListParams = () => {
setSort({ field: 'name', order: 'ASC' });
// The 3rd parameter disables the debounce ⤵
setFilters({ is_published: true }, undefined, false);
};
```
9 changes: 0 additions & 9 deletions examples/demo/src/orders/NbItemsField.tsx

This file was deleted.

16 changes: 7 additions & 9 deletions examples/demo/src/orders/OrderList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
} from 'react-admin';
import { useMediaQuery, Divider, Tabs, Tab, Theme } from '@mui/material';

import NbItemsField from './NbItemsField';
import CustomerReferenceField from '../visitors/CustomerReferenceField';
import AddressField from '../visitors/AddressField';
import MobileGrid from './MobileGrid';
Expand Down Expand Up @@ -85,8 +84,7 @@ const TabbedDatagrid = () => {
setFilters &&
setFilters(
{ ...filterValues, status: value },
displayedFilters,
false // no debounce, we want the filter to fire immediately
displayedFilters
);
},
[displayedFilters, filterValues, setFilters]
Expand Down Expand Up @@ -142,9 +140,9 @@ const TabbedDatagrid = () => {
>
<AddressField />
</ReferenceField>
<NbItemsField
<NumberField
source="basket.length"
label="resources.commands.fields.nb_items"
textAlign="right"
/>
<NumberField
source="total_ex_taxes"
Expand Down Expand Up @@ -193,9 +191,9 @@ const TabbedDatagrid = () => {
>
<AddressField />
</ReferenceField>
<NbItemsField
<NumberField
source="basket.length"
label="resources.commands.fields.nb_items"
textAlign="right"
/>
<NumberField
source="total_ex_taxes"
Expand Down Expand Up @@ -248,9 +246,9 @@ const TabbedDatagrid = () => {
>
<AddressField />
</ReferenceField>
<NbItemsField
<NumberField
source="basket.length"
label="resources.commands.fields.nb_items"
textAlign="right"
/>
<NumberField
source="total_ex_taxes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ describe('useReferenceManyFieldController', () => {
type="text"
value={filterValues.q || ''}
onChange={event => {
setFilters({ q: event.target.value });
setFilters({ q: event.target.value }, undefined, true);
}}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export const useReferenceManyFieldController = <
);

const setFilters = useCallback(
(filters, displayedFilters, debounce = true) => {
(filters, displayedFilters, debounce = false) => {
if (debounce) {
debouncedSetFilters(filters, displayedFilters);
} else {
Expand Down
10 changes: 7 additions & 3 deletions packages/ra-core/src/controller/input/useReferenceParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export const useReferenceParams = ({
const displayedFilterValues = query.displayedFilters || emptyObject;

const debouncedSetFilters = useRef(
lodashDebounce((filter, displayedFilters) => {
lodashDebounce((filter, displayedFilters = undefined) => {
changeParams({
type: SET_FILTER,
payload: {
Expand All @@ -146,7 +146,7 @@ export const useReferenceParams = ({
}, []);

const setFilters = useCallback(
(filter, displayedFilters, debounce = true) => {
(filter, displayedFilters = undefined, debounce = false) => {
debounce
? debouncedSetFilters.current(filter, displayedFilters)
: changeParams({
Expand Down Expand Up @@ -310,7 +310,11 @@ interface Modifiers {
setPage: (page: number) => void;
setPerPage: (pageSize: number) => void;
setSort: (sort: SortPayload) => void;
setFilters: (filters: any, displayedFilters: any) => void;
setFilters: (
filters: any,
displayedFilters?: any,
debounce?: boolean
) => void;
hideFilter: (filterName: string) => void;
showFilter: (filterName: string, defaultValue: any) => void;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const BookListView = () => {
});
};
const toggleFilter = () => {
setFilters(filterValues.q ? {} : { q: 'The ' }, null);
setFilters(filterValues.q ? {} : { q: 'The ' });
};

return (
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/controller/list/ListBase.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const BookListView = () => {
// the last parameter is debounce false
// without it, the filter change overrides any other list param change
// see https://github.com/marmelab/react-admin/issues/4189
setFilters(value.filterValues, null, false);
setFilters(value.filterValues, undefined, false);
}
if (value.page !== page) {
setPage(value.page);
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/controller/list/useList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export const useList = <RecordType extends RaRecord = any>(
[setDisplayedFilters, setFilterValues]
);
const setFilters = useCallback(
(filters, displayedFilters) => {
(filters, displayedFilters = undefined) => {
setFilterValues(removeEmpty(filters));
if (displayedFilters) {
setDisplayedFilters(displayedFilters);
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/controller/list/useListController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ export interface ListControllerResult<RecordType extends RaRecord = any> {
selectedIds: RecordType['id'][];
setFilters: (
filters: any,
displayedFilters: any,
displayedFilters?: any,
debounce?: boolean
) => void;
setPage: (page: number) => void;
Expand Down
8 changes: 6 additions & 2 deletions packages/ra-core/src/controller/list/useListParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ export const useListParams = ({
}, debounce);

const setFilters = useCallback(
(filter, displayedFilters, debounce = true) =>
(filter, displayedFilters = undefined, debounce = false) =>
debounce
? debouncedSetFilters(filter, displayedFilters)
: changeParams({
Expand Down Expand Up @@ -409,7 +409,11 @@ interface Modifiers {
setPage: (page: number) => void;
setPerPage: (pageSize: number) => void;
setSort: (sort: SortPayload) => void;
setFilters: (filters: any, displayedFilters: any) => void;
setFilters: (
filters: any,
displayedFilters?: any,
debounce?: boolean
) => void;
hideFilter: (filterName: string) => void;
showFilter: (filterName: string, defaultValue: any) => void;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/form/choices/ChoicesContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export type ChoicesContextValue<RecordType extends RaRecord = any> = {
selectedChoices: RecordType[];
setFilters: (
filters: any,
displayedFilters: any,
displayedFilters?: any,
debounce?: boolean
) => void;
setPage: (page: number) => void;
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-ui-materialui/src/input/AutocompleteInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ If you provided a React element for the optionText prop, you must also provide t
return;
}

setFilters(filterToQuery(filter), undefined, true);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This didn't make sense as the call is debounced two lines earlier. The previous code caused a double debounce.

setFilters(filterToQuery(filter));
}, debounceDelay),
[debounceDelay, setFilters, setFilter]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ describe('<FilterButton />', () => {
filterValues: {},
};

beforeAll(() => {
window.scrollTo = jest.fn();
});

afterAll(() => {
jest.clearAllMocks();
});

describe('filter selection menu', () => {
it('should display only hidden filters', () => {
const hiddenFilter = (
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-ui-materialui/src/list/filter/FilterButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export const FilterButton = (props: FilterButtonProps): JSX.Element => {
{hasFilterValues && (
<MenuItem
onClick={() => {
setFilters({}, {}, false);
setFilters({}, {});
setOpen(false);
}}
>
Expand Down
11 changes: 10 additions & 1 deletion packages/ra-ui-materialui/src/list/filter/FilterForm.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ describe('<FilterForm />', () => {
displayedFilters: {},
};

beforeAll(() => {
window.scrollTo = jest.fn();
});

afterAll(() => {
jest.clearAllMocks();
});

it('should display correctly passed filters', () => {
const setFilters = jest.fn();
const filters = [
Expand Down Expand Up @@ -111,7 +119,8 @@ describe('<FilterForm />', () => {
await waitFor(() => {
expect(setFilters).toHaveBeenCalledWith(
{ title: 'foo' },
{ title: true }
{ title: true },
true
);
});
});
Expand Down
4 changes: 2 additions & 2 deletions packages/ra-ui-materialui/src/list/filter/FilterForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ export const FilterForm = (props: FilterFormProps) => {
if (get(values, name) === '') {
const newValues = cloneDeep(values);
unset(newValues, name);
setFilters(newValues, displayedFilters);
setFilters(newValues, displayedFilters, true);
} else {
setFilters(values, displayedFilters);
setFilters(values, displayedFilters, true);
}
}
});
Expand Down
Loading
Loading