react-table 有条件地 select 行
react-table conditionally select row
我们正在使用 react-table 的 useRowSelect
进行行选择。我们想对其进行扩展,以便我们可以根据特定条件定义不可选择的行,在这种情况下,它们将不可选择 - 无论是单击不可选择的行还是单击 Select All 时。
如有任何想法,我们将不胜感激。
不知道您是否找到了答案,但我 运行 遇到了类似的问题。
您可以使用条件语句非常简单地禁用行复选框。
在您的 useTable() 中,您将这样的道具传递给您的复选框:
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
])
},
)
然后在您的复选框组件中,您可以根据您的道具之一判断是否要禁用复选框(在这种情况下,我使用名为“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>
)
})
如果 res.Valide 为真,则复选框正常呈现,否则它被禁用,因此您将无法选中它。
我想出了一个完整的解决方案,方法是调整 an answer recently posted in this GitHub issue
在我的例子中,我需要它来处理分页和 Select 页/Select 全部(而不是仅 Select 全部)。
我设法创建了一个解决方案 - 基于 GitHub 问题线程的解决方案 - 它也支持这些要求。然后我将它提取为一个易于重用的辅助函数。下面是辅助函数,后面是用法示例。
辅助函数
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,
}
}
用法示例:
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} />
},
// ...
]
我们正在使用 react-table 的 useRowSelect
进行行选择。我们想对其进行扩展,以便我们可以根据特定条件定义不可选择的行,在这种情况下,它们将不可选择 - 无论是单击不可选择的行还是单击 Select All 时。
如有任何想法,我们将不胜感激。
不知道您是否找到了答案,但我 运行 遇到了类似的问题。 您可以使用条件语句非常简单地禁用行复选框。
在您的 useTable() 中,您将这样的道具传递给您的复选框:
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
])
},
)
然后在您的复选框组件中,您可以根据您的道具之一判断是否要禁用复选框(在这种情况下,我使用名为“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>
)
})
如果 res.Valide 为真,则复选框正常呈现,否则它被禁用,因此您将无法选中它。
我想出了一个完整的解决方案,方法是调整 an answer recently posted in this GitHub issue
在我的例子中,我需要它来处理分页和 Select 页/Select 全部(而不是仅 Select 全部)。
我设法创建了一个解决方案 - 基于 GitHub 问题线程的解决方案 - 它也支持这些要求。然后我将它提取为一个易于重用的辅助函数。下面是辅助函数,后面是用法示例。
辅助函数
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,
}
}
用法示例:
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} />
},
// ...
]