mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
3.9k stars 1.19k forks source link

[data grid] Add support for async valueOptions callback #13240

Open DoubleDebug opened 1 month ago

DoubleDebug commented 1 month ago

Summary

One of the columns in my DataGrid component is a singleSelect column. In order to provide an array of options for this column, I'm using the valueOptions prop.

The type for the valueOptions prop for DataGrid columns currently looks like this:

valueOptions?: Array<ValueOptions> | ((params: GridValueOptionsParams<R>) => Array<ValueOptions>);

This means that you can directly pass an array of options, or you can pass a function that returns said array. This is great!

However, if I want to fetch these options from an API, I need the callback function to be asynchronous and I need to await the results. This currently isn't supported by the DataGrid component.

If I pass an async function to the valueOptions prop, I get the following error:

valueOptions.map is not a function

Examples

CodeSandbox link: https://codesandbox.io/p/sandbox/mui-datagrid-row-editing-forked-8zqx65?file=%2Fsrc%2Fdata%2Fcolumns.js

Take a look at the provided example. Open the columns.js file and look at line 31. You can see both the current solution and the desired solution.

Motivation

My company has an application with a lot of data tables, so we're using the DataGrid component for all of them. There's a lot of singleSelect columns and most of the time, the options are provided by a backend service.

We're currently solving this problem by implementing a custom renderEditCell component. However, it would be great if there was a built-in way of doing this!

Search keywords: DataGrid singleSelect valueOptions

michelengelen commented 1 month ago

Hey @DoubleDebug and thanks for raising this. I will add this to the board as it seems like a valid enhancement request. Thanks for taking the time to open an issue! 🙇🏼

noah-lee commented 2 weeks ago

Hey @DoubleDebug, I had a similar use case and my current workaround was to use a custom hook. Here's a simplified version:

const useAsyncValueOptions = (params: GridValueOptionsParams): ValueOptions[] => {
  const { data, isLoading } = useQuery(...); // Using react-query here but you can work with other solutions 

  if (isLoading) {
    return [{ value: '', label: 'Loading...' }];
  }

  return data; // Format to { value: ..., label: ... }[]
}
DoubleDebug commented 1 week ago

@noah-lee Hey, thanks for taking the time to reply.

I see your point, this is a good workaround. I have to point out a few things, though. If you were to use this method, you would pass the data directly to the valueOptions prop - you wouldn't pass the hook. Here's the usage of your hook:

const useColumns = () => {
  const options = useAsyncValueOptions();

  return [
    ... // other columns here
    {
      field: 'job-title',
      valueOptions: options
    }   
  ];
}

If you instead pass the useAsyncValueOptions function to the valueOptions prop, that wouldn't work. It also violates react's rules of hooks.

const useColumns = () => {
  return [
    ... // other columns here
    {
      field: 'job-title',
      valueOptions: useAsyncValueOptions // won't work
    }   
  ];
}

This means that your parameter params: GridValueOptionsParams is not necessary.


The other thing is that the useAsyncValueOptions hook is a generic hook that is supposed to extend the functionality of the DataGrid component. This is something that, in my opinion, should be supported by the library itself. This is why I opened this issue in the first place. I hope you understand what I want to say.