我正在尝试做的事情
我有一个要向其提供数据的 DataTable。DataTable 的列是根据提供的数据动态创建的。我想根据列包含的数据类型对每个列应用过滤器。例如,我希望范围过滤器可用于数值数据,并且唯一值的切换下拉列表可用于具有名称列表的列。在这个问题中,为简单起见,我仅以动态方式分配 DropDown 过滤器。
我遇到的问题
我无法以动态方式为每个 Column 组件提供过滤器所需的道具和状态。
我的问题
无法在表格中使用自定义功能组件(请参阅下面的我尝试过的内容):
如何动态创建具有需要自己的状态和处理程序的过滤器的列?在状态正常运行的情况下,我无法找到一种方法来做到这一点。
用例背景
- 我有一个接受 Columns 作为子项的 DataTable
- 可以将列配置为使用过滤器(即搜索框,或可以打开和关闭的唯一值的下拉列表)
- 过滤器需要:
- 一个 filterElement 函数,它返回过滤器类型的 JSX(IE:如果过滤器是下拉列表,则 filterElement 包含一个 MultiSelect)
- filterElement 需要
- 跟踪 selectedOptions 的状态
- 修改 selectedOptions 的 onChange
- onChange 需要
- 对数据表的引用
- onChange 需要
- filterElement 需要
- 一个 filterElement 函数,它返回过滤器类型的 JSX(IE:如果过滤器是下拉列表,则 filterElement 包含一个 MultiSelect)
当我们提前知道列时,以静态方式将下拉过滤器应用于列的示例
export default function DataTableFilterClassTest() {
const [tableData, setTableData] = useState(data); // Data imported from elsewhere
const dt = useRef(null);
const [selectedOptions, setSelectedOptions] = useState(null)
// Hardcoded some options which are available in the data
const options = [{ option: 1000 }, { option: 1001 }, { option: 1002 }, { option: 1003 }]
const onOptionsChange = (e) => {
// 2nd param 'id' is a reference to the field header this filter applies to
// Hardcoded for testing purposes
dt.current.filter(e.value, 'id', 'in')
setSelectedOptions(e.value)
}
const filterElement = () => {
return (
<React.Fragment>
<MultiSelect
value={selectedOptions}
options={options}
onChange={onOptionsChange}
optionLabel="option"
optionValue="option"
className="p-column-filter"
/>
</React.Fragment>
)
}
return (
<div className="datatable-filter-demo">
<div className="card">
<DataTable ref={dt} value={tableData} paginator rows={10}
className="p-datatable-customers"
emptyMessage="No customers found."
>
<Column field="id" header="id" filter filterElement={filterElement} />
</DataTable>
</div>
</div>
);
}
问题
这在我们事先知道列的静态情况下工作得很好,但是当我尝试根据我的数据动态创建列时就会出现问题。我不知道如何为每个动态列创建 filterElement 和 filterElement 所需的状态和 onChange。
// ....
const columnFieldsForMapping = ['id', 'company']
const mappedColumns = columnFieldsForMapping.map((field) => {
// Need to create a new filterElement
// filterElement requires its own onChange to modify selected options,
// and value (a state object, to store the selected options)
return (
<Column key={field} field={field} header={field} filter filterElement={HOW DO I CREATE THIS FOR EACH COLUMN} />
)
})
return (
<div className="datatable-filter-demo">
<div className="card">
<DataTable ref={dt} value={tableData} paginator rows={10}
className="p-datatable-customers"
emptyMessage="No customers found."
>
{mappedColumns}
</DataTable>
</div>
</div>
);
}
我试过的
创建一个 CustomColumn 组件,扩展 Column 组件
我的第一个解决方案是创建一个 CustomColumn 功能组件,它扩展了 PrimeReact 的列组件。CustomColumn 组件将包含 filterElement 及其所需的状态和功能。它将返回一个带有应用的 filterElement 属性的 Column 组件。不幸的是,我后来了解到 DataTable 不能接受除 Column 组件之外的任何组件。它不会接受扩展原始列组件的反应功能组件。这是 github 问题,指出这是不可能的:https ://github.com/primefaces/primereact/issues/644#issuecomment-790648272
CustomDropDownColumn.js
import React, { useState, useEffect } from 'react'
import { Column } from 'primereact/column';
import { MultiSelect } from 'primereact/multiselect';
export default function CustomDropDownColumn(props) {
const [state, setState] = useState([])
const onChange = (e) => {
props.dt.current.filter(e.value, props.field, 'in')
setState(e.value)
}
const filterElement = <MultiSelect
value={state}
options={props.options}
onChange={onChange}
optionLabel="option"
optionValue="option"
className="p-column-filter"
/>
return (
<Column field={props.field} header={props.field} filter filterElement={filterElement} />
)
}
使用自定义下拉列:
// ....
return (
<div className="datatable-filter-demo">
<div className="card">
<DataTable ref={dt} value={tableData} paginator rows={10}
className="p-datatable-customers"
emptyMessage="No customers found."
>
<CustomDropDownColumn dt={dt} options={options} field={field}/>
</DataTable>
</div>
</div>
);
// ....
创建一个基于类的组件来封装过滤器逻辑。从此类公开 filterElement 并将其作为 Column 组件中的道具应用
接下来,我尝试为每种过滤器类型创建一个类。过滤器类将存储 filterElement 及其所有必需的状态和功能。我的意图是在映射期间为每一列实例化一个新的 DropDownFilter 类,并将其公开的 filterElement 方法提供给正在映射的列。这不起作用,因为在 React 中以这种方式创建类不会挂载该类。因为它已卸载,所以我无法使用 DropDownFilter 类调用 setState。
DropDownFilter.js
import React from 'react'
import { MultiSelect } from 'primereact/multiselect';
class DropDownFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedOptions: null,
}
}
onOptionsChange = (e) => {
this.props.dt.current.filter(e.value, this.props.field, 'in');
this.setState({ selectedOptions: e.value })
}
filterElement = () => {
return (
<React.Fragment>
<MultiSelect
value={this.state.selectedOptions}
options={this.props.options}
onChange={this.onOptionsChange}
optionLabel="option"
optionValue="option"
className="p-column-filter"
/>
</React.Fragment>
)
}
render() {
return (null)
}
}
export default DropDownFilter;
数据表
// ....
const columnFieldsForMapping = ['id', 'company']
const mappedColumns = columnFieldsForMapping.map((field) => {
// We will map a drop down filter to each column
// Get unique values in column to supply to dropdowns
let options = tableData.map(x => {
const obj = {}
obj.option = x[field]
return obj;
})
options = uniq(options)
// Instantiate instance of DropDownFilter class to use with mapped column
const newFilter = new DropDownFilter({ dt: dt, field: field, options: options })
return (
<Column key={field} field={field} header={field} filter filterElement={newFilter.filterElement} />
)
})
return (
<div className="datatable-filter-demo">
<div className="card">
<DataTable ref={dt} value={tableData} paginator rows={10}
className="p-datatable-customers"
emptyMessage="No customers found."
>
{mappedColumns}
</DataTable>
</div>
</div>
);
}