简体   繁体   中英

react-table conditionally select row

We're using react-table 's useRowSelect for row selection. We want to extend it such that we can define rows that are non-selectable based on a certain condition, in which case they will not be selectable - neither when clicking on the non-selectable row nor when clicking Select All.

Any ideas would be greatly appreciated.

Dunno if you found your answer since but i ran into kinda a similar issue. You can use conditionnal statement to disable a row check box quite simply.

in your useTable() you pass your props like this to your checkbox:

DataTable.js

const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    selectedFlatRows,
    state: { selectedRowIds },
} = useTable(
    {
    columns,
    data,
    
    initialState: {
      selectedRowIds: INITIAL_SELECTED_ROW_IDS
      
    }

    },
    useRowSelect,
    hooks => {
    hooks.visibleColumns.push(columns => [
        {
        id: 'selection',
        Header: ({ getToggleAllRowsSelectedProps }) => (
            <Checkbox {...getToggleAllRowsSelectedProps()} />
        ),
        Cell: ({ row }) => <><Checkbox {...row.getToggleRowSelectedProps()} {...row.values} />  </> 
//HERE : You pass your rows' values to your component
        },
        ...columns
    ])
    },
)

then in your checkbox component you can tell if you want the checkbox to be disabled or not based on one of your props (in this case i use one of the rows value called "valide")

CheckBox.js

import React, {useState} from 'react'

export const Checkbox = React.forwardRef(({ indeterminate, ...rest }, props, ref) => {
  const defaultRef = React.useRef()
  const resolvedRef = ref || defaultRef

  React.useEffect(() => {
    resolvedRef.current.indeterminate = indeterminate
  }, [resolvedRef, indeterminate])

  return (
    
    <div className="d-flex justify-content-around">
      {
      rest.Valide 
//i check if res.Valide is true or false (res.Valide is one of my row column's value which takes a boolean)
      ? <><input type='checkbox' ref={resolvedRef} {...rest} className="mx-auto" /></>
      : <><input type='checkbox' ref={resolvedRef} {...rest} className="mx-auto" disabled/></>
      }
    </div>
  )
})

if res.Valide is true then the checkbox is rendered normally, else it's disabled so you won't be able to check it.

I managed to come up with a full solution to this problem, by adapting an answer recently posted in this GitHub issue

In my case, I needed this to work with pagination and with Select Page / Select All (as opposed to only Select All).

I managed to create a solution - based on the solution from the GitHub issue thread - which supports these requirements as well. I then extracted it to an easily reusable helper function. Below is the helper function followed by an example of usage .

The helper function

import { HeaderProps, Row } from 'react-table'

interface GetConditionalSelectHeaderCheckboxProps {
  /** react-table's header props */
  headerProps: React.PropsWithChildren<HeaderProps<any>>
  /** A predicate - based on your business logic - to determine whether a given row should be selectable */
  checkIfRowIsSelectable: (row: Row<any>) => boolean
  /** Whether to allow page selection. Default: true */
  shouldSelectPage?: boolean
}

/**
 * A convenience method for react-table headers for allowing conditional select
 * @param headerProps react-table's header props
 * @param checkIfRowIsSelectable A predicate - based on your business logic - to determine whether a given row should be selectable
 * @param shouldSelectPage Whether to allow page selection. Default: true
 * @returns Modified `checkboxProps` to enforce the conditional select
 */
export const getConditionalSelectHeaderCheckboxProps = ({
  headerProps,
  checkIfRowIsSelectable,
  shouldSelectPage = true,
}: GetConditionalSelectHeaderCheckboxProps) => {
  // Note that in my comments I differentiate between the standard logic and the logic for the conditional select
  const checkIfAllSelectableRowsSelected = (rows: Row<any>[]) =>
    rows.filter(checkIfRowIsSelectable).every(row => row.isSelected)
  // Standard: Here we define the selection type for the next click: Select Page / Select All
  const isSelectPage =
    shouldSelectPage &&
    headerProps.page
      // For conditional select: Filter the rows based on your business logic
      .filter(checkIfRowIsSelectable)
      // Standard: `isSelectPage === true` if some of the rows are not yet selected
      // This (standard) logic might be confusing to understand at first, but - as a side note - the idea is as follows:
      // This is the variable that defines whether the header props that will be received FOR THE NEXT CLICK will be for Select Page or for Select All
      // Try to play this out in your head:
      //  - Initially, none of the rows are selected, so when we clicking the button initially, we will select only the (selectable) rows on the page (i.e. Select Page), hence the next click will be for Select All, hence `isSelectPage` will be `false`
      //  - When clicking again, we will select the rest of the (selectable) rows (i.e. Select All). The next click will again be Select All (for de-selecting all), hence `isSelectPage` will be `false`
      //  - Finally, when clicking again, we will de-select all rows. The next click will be for Select Page, hence `isSelectPage` will `true`
      .some(row => !row.isSelected)

  // Standard: Get the props based on Select Page / Select All
  const checkboxProps = isSelectPage
    ? headerProps.getToggleAllPageRowsSelectedProps()
    : headerProps.getToggleAllRowsSelectedProps()

  // For conditional select: The header checkbox should be:
  //   - checked if all selectable rows are selected
  //   - indeterminate if only some selectable rows are selected (but not all)
  const disabled = headerProps.rows.filter(checkIfRowIsSelectable).length === 0
  const checked =
    !disabled && checkIfAllSelectableRowsSelected(headerProps.rows)
  const indeterminate = !checked && headerProps.rows.some(row => row.isSelected)

  // For conditional select: This is where the magic happens
  const onChange = () => {
    // If we're in Select All and all selectable rows are already selected: deselect all rows
    if (!isSelectPage && checkIfAllSelectableRowsSelected(headerProps.rows)) {
      headerProps.rows.forEach(row => {
        headerProps.toggleRowSelected(row.id, false)
      })
    } else {
      // Otherwise:
      // First, define the rows to work with: if we're in Select Page, use `headerProps.page`, otherwise (Select All) use headerProps.rows
      const rows = isSelectPage ? headerProps.page : headerProps.rows
      // Then select every selectable row
      rows.forEach(row => {
        const checked = checkIfRowIsSelectable(row)
        headerProps.toggleRowSelected(row.id, checked)
      })
    }
  }

  // For conditional select: override checked, indeterminate and onChange - to enforce conditional select based on our business logic
  return {
    ...checkboxProps,
    checked,
    indeterminate,
    onChange,
    disabled,
  }
}

Example usage:

const columns = [
    {
        accessor: 'foo',
        Header(props) {
            const checkboxProps = getConditionalSelectHeaderCheckboxProps({
                headerProps: props,
                // Your business logic, e.g.
                checkIfRowIsSelectable: row => row.original.someData !== 'some value'
            })

            return <Checkbox {...checkboxProps} />
        },
    // ...
]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM