Skip to content

useDataGrid sends API requests on every keystroke causing performance issues and race conditions #7369

@e-muravev

Description

@e-muravev

Describe the bug

Describe the bug:

The useDataGrid hook from @refinedev/mui sends API requests on every filter change (including every keystroke in text filters). This causes several problems:

1.API flooding - multiple unnecessary requests while user is typing

2.Race conditions - slower responses can overwrite faster ones, resulting in incorrect data

3.Lost characters - when typing quickly, some characters may be lost due to request interference

4.Poor UX - UI feels laggy, especially on slower connections or large datasets

The hook lacks built-in debouncing for filter changes, which is a standard pattern in modern data grids.

Steps To Reproduce

Create a component using useDataGrid with text filtering:

import { useDataGrid } from '@refinedev/mui'
import { DataGrid, GridToolbar } from '@mui/x-data-grid'

export const UserList = () => {
  const { dataGridProps } = useDataGrid({
    resource: 'users',
  })

  return (
    <DataGrid
      {...dataGridProps}
      slots={{ toolbar: GridToolbar }}
    />
  )
}

Open browser DevTools → Network tab

Type "john" slowly into the quick filter or column filter

Observe: Each keystroke triggers a separate API request

Type quickly "john" → observe race conditions where results flicker

Expected behavior

API request should only be sent after the user stops typing (e.g., 500-1000ms debounce)

Should be configurable via option like filterDebounceMs?: number

*Proposed Solution:
Add built-in debouncing to useDataGrid:

const { dataGridProps } = useDataGrid({
  resource: 'users',
  filterDebounceMs: 500, // Optional, default 500ms
})

For now I use a workaround in my project

export const useDebouncedDataGrid = <
  TQueryFnData extends BaseRecord = BaseRecord,
  TError extends HttpError = HttpError,
  TSearchVariables = unknown,
  TData extends BaseRecord = TQueryFnData,
>(
  options?: UseDataGridProps<TQueryFnData, TError, TSearchVariables, TData>,
) => {
  const [filterModel, setFilterModel] = useState<GridFilterModel>({ items: [] })

  const dataGridReturnObject = useDataGrid({
    ...options,
  })

  const debouncedSetFilters = useMemo(
    () => debounce(dataGridReturnObject.setFilters, 1000),
    [dataGridReturnObject.setFilters],
  )

  const handleFilterModelChange = useCallback(
    (newFilterModel: GridFilterModel) => {
      setFilterModel(newFilterModel)
      debouncedSetFilters(newFilterModel.items as CrudFilter[])
    },
    [debouncedSetFilters],
  )

  dataGridReturnObject.dataGridProps.filterModel = filterModel
  dataGridReturnObject.dataGridProps.onFilterModelChange =
    handleFilterModelChange

  return dataGridReturnObject
}

Packages

System:

  • OS: Linux 5.15 Ubuntu 20.04.6 LTS (Focal Fossa)
  • CPU: (8) x64 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz

Binaries:

  • Node: 22.15.0 - /usr/local/bin/node
  • Yarn: 1.22.18 - /home/evgeny/.yarn/bin/yarn
  • npm: 11.6.2 - /usr/local/bin/npm

Browsers:

  • Chrome: 137.0.7151.55
  • Firefox: 136.0

Refine Packages:

  • @refinedev/cli: 2.16.51
  • @refinedev/core: 5.0.6
  • @refinedev/devtools: 2.0.3
  • @refinedev/kbar: 2.0.1
  • @refinedev/mui: 7.0.1
  • @refinedev/nestjsx-crud: 6.0.1
  • @refinedev/react-hook-form: 5.0.3
  • @refinedev/react-router: 2.0.3

Additional Context

  • Current behavior: Request per keystroke

  • Expected: Request after typing pause

  • Workaround: Custom wrapper hook (but should be built-in)

  • Affects: All refine MUI users with text filtering

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions