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
之外,因为每次重新渲染父级都会创建它的新实例,这会导致失去焦点。
我正在尝试从这个例子中复制全局过滤器实现: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
之外,因为每次重新渲染父级都会创建它的新实例,这会导致失去焦点。