skip to Main Content

In a React-Admin Datagrid component, I want to set all of the list items to be individually selectable (isRowSelectable) based on all the rows in aggregate.

That is, all items should be selectable (if the status property is the same for all of them); OR all items should NOT be selectable (if the status property is NOT the same for all).

The code below does that, but has one problem. When I try to select all items using the checkbox at the top of the Datagrid, I get an error (Invalid hook call ... handleSelectAll DatagridHeader.tsx:65).

I don’t get that error when I select the list items individually.

const checkAllStatuses = () => {
  const { data } = useListContext();
  const uniqueStatuses = [...new Set(data.map((item) => item.status))];
  return uniqueStatusStates.length === 1;
};

const EmployeeList = (props) => {
  return (
    <List title="Employees">
      <Datagrid isRowSelectable={checkAllStatuses}>
        ...
      </Datagrid>
    </List>
  );
};

How can I prevent the "Invalid hook call…" error when I select all the items collectively using the button at the top?

Alternatively, is there a way to disable or hide the "select all" checkbox in the Datagrid header – but only when the status property is not the same for all items?

2

Answers


  1. From what I understood, you want to

    1. Add a state to get your currently selected status
    2. Create a function to overwrite the rows selection on the Datagrid (see https://github.com/marmelab/react-admin/issues/2000#issuecomment-840594240 to overwrite the selection)
    3. Select everything else that has the same status when the user checks something
    4. Also set the currently selected status when the user checks something
    5. Use the currently selected status to also disable the checks on other rows

    Which translates to:

    import { useRecordSelection } from 'react-admin' 
    
    // step 2
    const useSetSelectedRecords = (resource: string, ids: string[] | undefined) => {
      const [, { select }] = useRecordSelection(resource)
      useEffect(() => {
        ids && select(ids)
      }, [select, ids])
    }
    
    const EmployeeList = (props) => {
    
      const { data } = useListContext();
      const [currentStatus, setCurrentStatus ] = useState(undefined);// step 1
    
      return (
        <List title="Employees">
          <Datagrid isRowSelectable={(record) =>
            // step 5
            currentStatus && record.status === currentStatus
          }>
            ...
          </Datagrid>
        </List>
      );
    };
    

    And then call the code below (step 3) and the setCurrentStatus function (step 4) when the user interacts with the checkbox or as an effect for when the selected ids change.

      useSetSelectedRecords(
        props.resource,
        data
          .filter(record => record.status === currentStatus)
          .map(record => record.id)
      )
    

    And don’t forget to call setCurrentStatus(undefined) when you remove all selections too to re-enable the row selection for all rows.

    Login or Signup to reply.
  2. Possible implementation:

    const DatagridCheck = () => {
      let checkAll = false
      const { data } = useListContext()
    
      if (data) {
        const uniqueStatuses = new Set(data.map((item) => item.status)) 
        checkAll = uniqueStatuses.size === 1
      }
    
      return (
        <Datagrid isRowSelectable={() => checkAll}>
          ...
        </Datagrid>
      )
    }
    
    const EmployeeList = (props) => {
      return (
        <List title="Employees">
          <DatagridCheck />
        </List>
      )
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search