React-table 过滤器在每次击键后失去焦点

React-table filter losing focus after each keystroke

我正在尝试从这个例子中复制全局过滤器实现:https://react-table.tanstack.com/docs/examples/filtering我已经复制了所有代码并且过滤工作正常。但是由于某种原因,每当我在输入框中输入一个字符时,它就会失去焦点,我需要再次点击输入框才能继续输入。

这是整个 table 文件:

import React, { useState } from "react";
import {
  useTable,
  useSortBy,
  useGlobalFilter,
  useAsyncDebounce
} from "react-table";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
  faSortUp,
  faSortDown,
  faCheck,
  faEllipsisV
} from "@fortawesome/free-solid-svg-icons";
import { Scrollbars } from "rc-scrollbars";

const IngredientsTable = (props) => {
  const { data, selectedIngredient } = props;

  const [showIngredientCheckID, setShowIngredientCheckID] = useState(-1);

  const columns = React.useMemo(
    () => [
      {
        Header: "Name",
        accessor: "col1" // accessor is the "key" in the data
      },
      {
        Header: "",
        accessor: "col2"
      },
      {
        Header: "Item Number",
        accessor: "col3" // accessor is the "key" in the data
      },
      {
        Header: "EPA Number",
        accessor: "col4"
      },
      {
        Header: "Category",
        accessor: "col5"
      },
      {
        Header: "Modified",
        accessor: "col6"
      }
    ],
    []
  );

  // Define a default UI for filtering
  const GlobalFilter = ({
    preGlobalFilteredRows,
    globalFilter,
    setGlobalFilter
  }) => {
    const count = preGlobalFilteredRows.length;
    const [value, setValue] = useState(globalFilter);
    const onChange = useAsyncDebounce((value) => {
      setGlobalFilter(value || undefined);
    }, 200);

    return (
      <span>
        Filter Ingredients:{" "}
        <input
          value={value || ""}
          onChange={(e) => {
            setValue(e.target.value);
            onChange(e.target.value);
          }}
          placeholder={`${count} records...`}
          style={{
            fontSize: "1.1rem",
            border: "0"
          }}
        />
      </span>
    );
  };

  // Define a default UI for filtering
  function DefaultColumnFilter({
    column: { filterValue, preFilteredRows, setFilter }
  }) {
    const count = preFilteredRows.length;

    return (
      <input
        value={filterValue || ""}
        onChange={(e) => {
          setFilter(e.target.value || undefined); // Set undefined to remove the filter entirely
        }}
        placeholder={`Search ${count} records...`}
      />
    );
  }

  const defaultColumn = React.useMemo(
    () => ({
      // Let's set up our default Filter UI
      Filter: DefaultColumnFilter
    }),
    []
  );

  const filterTypes = React.useMemo(
    () => ({
      // Or, override the default text filter to use
      // "startWith"
      text: (rows, id, filterValue) => {
        return rows.filter((row) => {
          const rowValue = row.values[id];
          return rowValue !== undefined
            ? String(rowValue)
                .toLowerCase()
                .startsWith(String(filterValue).toLowerCase())
            : true;
        });
      }
    }),
    []
  );

  const tableInstance = useTable(
    { columns, data, defaultColumn, filterTypes },
    useGlobalFilter, // useGlobalFilter!
    useSortBy
  );

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    state,
    preGlobalFilteredRows,
    setGlobalFilter
  } = tableInstance;

  // const showUserCheck = (id) => {};

  const thumbVertical = ({ style, ...props }) => {
    const finalStyle = {
      ...style,
      visibility: "hidden"
    };

    return <div style={finalStyle} {...props} />;
  };

  return (
    <div className={"table-container"}>
      <Scrollbars
        autoHeight
        autoHeightMin={0}
        autoHeightMax={"calc(100vh - 40px)"}
        renderThumbVertical={thumbVertical}
      >
        <>
          <div className={"row mx-auto my-2"}>
            <div className={"col-8"}>
              <GlobalFilter
                preGlobalFilteredRows={preGlobalFilteredRows}
                globalFilter={state.globalFilter}
                setGlobalFilter={setGlobalFilter}
              />
            </div>
          </div>
          <table
            {...getTableProps()}
            className={
              "table table-striped table-hover table-borderless ingredients-table"
            }
          >
            <thead>
              {headerGroups.map((headerGroup, i) => (
                <tr {...headerGroup.getHeaderGroupProps()} key={i}>
                  {headerGroup.headers.map((column, thInd) => (
                    // Add the sorting props to control sorting. For this example
                    // we can add them into the header props
                    <th
                      {...column.getHeaderProps(column.getSortByToggleProps())}
                      key={thInd}
                    >
                      {column.render("Header")}
                      {/* Add a sort direction indicator */}
                      <span>
                        {column.isSorted ? (
                          column.isSortedDesc ? (
                            <FontAwesomeIcon icon={faSortDown} size={"lg"} />
                          ) : (
                            <FontAwesomeIcon icon={faSortUp} size={"lg"} />
                          )
                        ) : (
                          ""
                        )}
                      </span>
                    </th>
                  ))}
                </tr>
              ))}
            </thead>

            <tbody {...getTableBodyProps()}>
              {rows.map((row, ind) => {
                // console.log({ row });
                prepareRow(row);

                return (
                  <tr
                    {...row.getRowProps()}
                    onClick={(e) =>
                      setTheSelectedIngredient(e, row.values.col1)
                    }
                    onMouseEnter={() => setShowIngredientCheckID(row.id)}
                    onMouseLeave={() => setShowIngredientCheckID(-1)}
                    key={ind}
                    className={`${
                      selectedIngredient.name === row.values.col1
                        ? "selected"
                        : ""
                    }`}
                    data-testid={"ingredient-row"}
                  >
                    {row.cells.map((cell, tdInd) => {
                      return (
                        <td {...cell.getCellProps()} key={tdInd}>
                          {tdInd === 0 ? (
                            selectedIngredient.name === row.values.col1 ? (
                              <>
                                <FontAwesomeIcon
                                  icon={faCheck}
                                  className={"white"}
                                />{" "}
                              </>
                            ) : showIngredientCheckID === row.id ? (
                              <>
                                <FontAwesomeIcon
                                  icon={faCheck}
                                  className={"gray"}
                                />{" "}
                              </>
                            ) : (
                              <>
                                <FontAwesomeIcon
                                  icon={faCheck}
                                  className={"clear"}
                                />{" "}
                              </>
                            )
                          ) : (
                            tdInd === 1 && (
                              <FontAwesomeIcon
                                icon={faEllipsisV}
                                className={"three-dots"}
                              />
                            )
                          )}
                          {cell.render("Cell")}
                        </td>
                      );
                    })}
                  </tr>
                );
              })}
            </tbody>
          </table>
        </>
      </Scrollbars>
    </div>
  );
};

export default IngredientsTable;

这是一个代码沙箱,您可以在其中看到问题的发生。 https://codesandbox.io/s/relaxed-benz-co8ub?file=/src/components/pieces/IngredientsTable.js:0-7960 在顶部,单击“100 条记录...”并尝试输入内容。焦点在每个字符之后离开。我不知道是什么原因造成的。

只需将 GlobalFilter 声明移到 IngredientsTable 之外,因为每次重新渲染父级都会创建它的新实例,这会导致失去焦点。

固定 CSB - https://codesandbox.io/s/bold-browser-mwuxd