You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 20:36:58