I am trying to add column filter on the header of the table but after each input typing on the field, it is losing the focus.
My main component: Here, I have initialized a local state: searchColArr
and all others state used in fetchData
are the redux state, which are being initialized using useSelectror
Category.jsx
const [searchColArr, setSearchColArr] = useState({
name: "",
});
//all these default value are either local state or redux state
function fetchData(
newPage = page,
newPerPage = perPage,
newOrder = order,
newDir = dir,
newSearchColArr = searchColArr,
newSearch = search
) {
dispatch(
listCategory(
newPage,
newPerPage,
newOrder,
newDir,
newSearchColArr,
newSearch
)
);
}
const headerRow = React.useMemo(
() => [
{
header: "Name",
name: "name",
filter: true,
sort: true,
},
{
header: "Action",
name: "id",
filter: false,
sort: false,
},
],
[]
);
const TBody = () => {
return (
<tbody key="tbody">
{list &&
list.map((row, i) => (
<tr key={row.id}>
<td>{row.name}</td>
<td>
<ActionColumn id={row.id} />
</td>
</tr>
))}
</tbody>
);
};
const handleColSearch = ({ currentTarget: input }) => {
setSearchColArr({
...searchColArr,
[input.name]: input.value,
});
};
useEffect(() => {
fetchData();
}, [searchColArr]);
return (
<div className="row">
<div className="col-md-12">
{/* REDUX TABLE STARTS */}
<ReduxTable
key="redux-table"
isLoading={isLoading}
headerRow={headerRow}
list={list}
page={page}
perPage={perPage}
order={order}
dir={dir}
total={total}
totalPage={totalPage}
searchColArr={searchColArr}
handlePageLengthChange={handlePageLengthChange}
handlePageChange={handlePageChange}
handleColSearch={handleColSearch}
TBody={TBody}
/>
{/* REDUX TABLE ENDS */}
</div>
</div>
);
And, inside Redux Table I am loading another component ReduxTableHeader
ReduxTable
const ReduxTable = ({
isLoading,
headerRow,
list,
page,
perPage,
order,
dir,
total,
totalPage,
searchColArr,
handlePageLengthChange,
handlePageChange,
handleColSearch,
TBody,
}) => {
return (
<div className="card-box">
{/* TABLE HEADER AND BODY just looping thorugh list*/}
<table
id="basicTable"
className="table table-bordered action-table table-striped table-hover mb-0"
>
{/* ReduxTableHeader */}
<ReduxTableHeader
key="redux-table-header"
headerRow={headerRow}
searchColArr={searchColArr}
handleColSearch={handleColSearch}
/>
<TBody />
</table>
</div>
);
};
ReduxTableHeader
return (
<thead key="thead">
<tr>
//list of header name
</tr>
<tr key="filter-row">
{headerRow &&
headerRow.map((v, i) => (
<th key={v.name}>
{v.filter ? (
<input
type="search"
className="form-control form-control-sm d-inline-block"
placeholder=""
aria-controls="datatable"
name={v.name}
value={searchColArr[v.name] || ""}
onChange={handleColSearch}
autoComplete="off"
key={"index" + i}
/>
) : (
""
)}
</th>
))}
</tr>
</thead>
);
This table header is the place where I am loading an input field, and whenever I type on this letter is just losing the auto focus. How could I fix it?
You are violating rules of React by creating nested components like Tbody
. This creates new component every render (even though function name stays the same), and causes that component to remount
In 99.999% of cases your input loses focus because one of its parent components remounts, thus creating a new component tree (and new input element) every time you type something. You are probably doing the same thing as you do with Tbody above in component tree, and your whole Category
component remounts every time you trigger fetchData
. To confirm you can comment out this useEffect
that triggers fetchData
Since input
component loses focus, it means it is rerendered. It happens only when one of input component has changed type or upper tree is changed. In this case react does complete rerender.
So I guess one of the parent components [ReduxTable, ReduxTableHeader] is defined on every render. like you do for TBody.
In this case React thinks it's new type of component and destorys previous tree and do complete rerender which causes performance lag and in your case make input lose focus.
Regarding the performance you should not define component inside function. It makes react foolish.
To prevent this you need to make sure all of input's parent components are defined outside of Render function. In this case outside of function component.
Please check this my code sandbox link which demonstrates 2 cases.
https://codesandbox.io/s/react-playground-rvikz?file=/src/App.js:567-572
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With