Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Input field losing focus on each character type - react

Tags:

reactjs

redux

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?

like image 906
Aayush Dahal Avatar asked May 12 '21 07:05

Aayush Dahal


2 Answers

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

like image 139
Max Avatar answered Oct 20 '22 20:10

Max


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.

  1. When you try to edit first row of inputs it doesn't lose focus
  2. When you try to edit other inputs which is in TBody, will lose focus Maybe same issue with you.

https://codesandbox.io/s/react-playground-rvikz?file=/src/App.js:567-572

like image 1
Zhang TianYu Avatar answered Oct 20 '22 21:10

Zhang TianYu