React Table独立表单日期范围过滤:点击提交触发过滤需求
React-Table 外部表单触发日期范围过滤问题
问题描述
我正在使用react-table开发日期范围过滤功能,已将过滤值移至表格外的独立表单中,希望点击「Filter Data」按钮提交表单时触发表格过滤操作。
相关代码
App.js
import "./styles.css"; import { useMemo } from "react"; import Table from "./mytrial/Table"; import ColumnData from "./mytrial/ColumnData"; import mockData from "./STATUS_DATA.json"; export default function App() { const data = useMemo(() => mockData, []); const columns = useMemo(() => ColumnData, []); return <Table data={data} columns={columns} />; }
Table.js
import { useTable, useSortBy, useGlobalFilter, useFilters, usePagination, } from "react-table"; import SharedAccordionTableFilter from "./Filter"; function Table({ data, columns }) { const { getTableProps, getTableBodyProps, headerGroups, page, prepareRow, setFilter, } = useTable( { columns, data }, useFilters, useGlobalFilter, useSortBy, usePagination ); return ( <> <SharedAccordionTableFilter headerGroups={headerGroups} setFilter={setFilter} /> <div className="card"> <div className="card-header"> <div className="d-flex justify-content-end"></div> </div> <div className="card-body"> <div className="table-responsive"> <table {...getTableProps()} className="table table-striped"> <thead> {headerGroups.map((headerGroup) => ( <tr {...headerGroup.getHeaderGroupProps()}> {headerGroup.headers.map((column) => ( <th {...column.getHeaderProps( column.getSortByToggleProps() )} > {column.render("Header")} {column.isSorted && (column.isSortedDesc ? ( <span className="psi-arrow-up text-end fw-bold fs-4 ms-2"></span> ) : ( <span className="psi-arrow-down text-end fw-bold fs-4 ms-2"></span> ))} </th> ))} </tr> ))} </thead> <tbody {...getTableBodyProps()}> {page.map((row) => { prepareRow(row); return ( <tr {...row.getRowProps()}> {row.cells.map((cell) => ( <td {...cell.getCellProps()}>{cell.render("Cell")}</td> ))} </tr> ); })} </tbody> </table> </div> </div> </div> </> ); } export default Table;
ColumnData.js
import { DateRangeColumnFilter, dateBetweenFilterFn } from "./MyFilters"; const ColumnData = [ { Header: "Data sent", accessor: "date", Filter: DateRangeColumnFilter, filter: dateBetweenFilterFn, }, { Header: "Company", accessor: "company", }, { Header: "GLN", accessor: "gln", }, { Header: "File Name", accessor: "fileName", }, { Header: "Items Sent", accessor: "itemSent", }, { Header: "Errors Received", accessor: "errorReceived", disableFilters: true, }, { Header: "Duration", accessor: "duration", }, { Header: "Error Message", accessor: "errorMessage", }, { Header: <span className="text-center">Status</span>, accessor: "status", Cell: ({ value }) => ( <span className={`bg-${value ? "success" : "danger"} p-2 d-block badge fs-6`} > {value ? "Success" : "Error"} </span> ), disableSortBy: true, }, ]; export default ColumnData;
Filter.js
import React from "react"; import { useForm } from "react-hook-form"; const SharedAccordionTableFilter = ({ headerGroups, setFilter }) => { const { register, handleSubmit, reset } = useForm(); const onSubmit = (data) => { console.log(data); const { inputStartDate, inputEndDate } = data; var startDate = new Date(inputStartDate); var stopDate = new Date(inputEndDate); }; const onReset = () => { headerGroups.forEach((headerGroup) => { headerGroup.headers.forEach((column) => { if (column.canFilter) { setFilter(column.id, ""); } }); reset(); }); }; return ( <> <form onSubmit={handleSubmit(onSubmit)}> <div id="defaultAccordion" className="accordion"> <div className="accordion-item"> <div id="AccFilter" className="accordion-header"> <button className="accordion-button collapsed fs-5 text-decoration-underline" type="button" data-bs-toggle="collapse" data-bs-target="#AccFilterCollapse" aria-expanded="true" aria-controls="AccFilterCollapse" > Filters </button> </div> <div id="AccFilterCollapse" className="accordion-collapse collapse" aria-labelledby="AccFilter" > <div className="accordion-body"> <div className="row g-3 row-cols-md-auto align-items-center"> <div className="col-12"> <label htmlFor="inputStartDate">Start Date</label> <input type="date" className="form-control" id="inputStartDate" {...register("inputStartDate")} /> </div> <div className="col-12"> <label htmlFor="inputEndDate">End Date</label> <input type="date" className="form-control" id="inputEndDate" {...register("inputEndDate")} /> </div> </div> </div> </div> </div> </div> <div className="d-flex justify-content-start gap-4"> <button className="btn btn-primary" type="submit"> Filter Data </button> <button className="btn btn-danger" type="button" onClick={onReset}> Clear All Filters </button> </div> </form> </> ); }; export default SharedAccordionTableFilter;
MyFilters.js
import React from "react"; export function dateBetweenFilterFn(rows, id, filterValues) { const sd = filterValues[0] ? new Date(filterValues[0]) : undefined; const ed = filterValues[1] ? new Date(filterValues[1]) : undefined; if (ed || sd) { return rows.filter((r) => { var dateAndHour = r.values[id].split(" "); var [year, month, day] = dateAndHour[0].split("-"); var date = [month, day, year].join("/"); var hour = dateAndHour[1]; var formattedData = date + " " + hour; const cellDate = new Date(formattedData); if (ed && sd) { return cellDate >= sd && cellDate <= ed; } else if (sd) { return cellDate >= sd; } else { return cellDate <= ed; } }); } else { return rows; } } export function DateRangeColumnFilter({ column: { filterValue = [], preFilteredRows, setFilter, id }, }) { const [min, max] = React.useMemo(() => { let min = preFilteredRows.length ? new Date(preFilteredRows[0].values[id]) : new Date(0); let max = preFilteredRows.length ? new Date(preFilteredRows[0].values[id]) : new Date(0); preFilteredRows.forEach((row) => { const rowDate = new Date(row.values[id]); min = rowDate <= min ? rowDate : min; max = rowDate >= max ? rowDate : max; }); return [min, max]; }, [id, preFilteredRows]); return ( <div> <input onChange={(e) => { const val = e.target.value; setFilter((old = []) => [val ? val : undefined, old[1]]); }} type="date" value={filterValue[0] || ""} /> {" to "} <input onChange={(e) => { const val = e.target.value; setFilter((old = []) => [ old[0], val ? val.concat("T23:59:59.999Z") : undefined, ]); }} type="date" value={filterValue[1]?.slice(0, 10) || ""} /> </div> ); }
解决方案
1. 修改表单提交逻辑(Filter.js)
在onSubmit函数中调用react-table的setFilter方法,将表单日期值按过滤函数要求的格式传递给目标列:
const onSubmit = (data) => { const { inputStartDate, inputEndDate } = data; const filterValues = []; if (inputStartDate) { filterValues[0] = inputStartDate; } if (inputEndDate) { // 为结束日期添加当日最后时刻,确保包含当天所有时段的数据 filterValues[1] = `${inputEndDate}T23:59:59.999Z`; } // 针对accessor为"date"的列设置过滤值 setFilter("date", filterValues); };
2. 修复重置过滤逻辑(Filter.js)
将原来传递空字符串的重置方式改为传递空数组,匹配日期范围过滤的格式要求:
const onReset = () => { headerGroups.forEach((headerGroup) => { headerGroup.headers.forEach((column) => { if (column.canFilter) { setFilter(column.id, []); } }); }); reset(); };
3. 可选:移除表头过滤器
如果不需要表头的内置过滤控件,可在ColumnData.js中移除对应配置:
{ Header: "Data sent", accessor: "date", // Filter: DateRangeColumnFilter, 移除该行 filter: dateBetweenFilterFn, },
修改完成后,点击「Filter Data」按钮即可触发日期范围过滤,重置按钮也能正确清空所有过滤条件。
内容的提问来源于stack exchange,提问作者Philomath
相关产品推荐
相关产品推荐

