每输入一个字符,输入框就失去焦点 - React

7

我想在表头上添加列筛选器,但在每次输入后,它会失去焦点。

我的主要组件:searchColArr 是一个本地状态,而 fetchData 中使用的所有其他状态都是 redux 状态,它们使用 useSelector 进行初始化。

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>
);

在Redux Table中,我正在加载另一个组件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>
    );
};

Redux表头

    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>
    );

我正在加载一个输入字段的表头,但每当我在这个字母上打字时,它就会失去自动聚焦。我该如何修复它?


很可能您的组件在每次调用handleColSearch后都会重新渲染。如果没有更多上下文,将很难进一步诊断。 - Dave Newton
请告诉我您想查看哪个文件。 - Aayush Dahal
1
请参见 https://dev59.com/KWEh5IYBdhLWcg3wTB72。 - ale917k
如果可能的话,请添加一个代码沙盒链接。 - Niraj Patel
3个回答

2
你正在违反React的规则,通过创建像 Tbody 这样的嵌套组件。这会在每次渲染时创建新的组件(即使函数名称保持不变),并导致该组件重新挂载。
99.999% 的情况下,你的输入框失去焦点是因为其父组件重新挂载,从而每次输入内容都会创建一个新的组件树(和新的输入框元素)。你可能在组件树中做了与上述Tbody相同的事情,并且每次触发fetchData时,整个Category组件都会重新挂载。为了确认,你可以将触发fetchDatauseEffect注释掉。

同时将keys设置为索引也会导致重新挂载。还有其他情况。 - Daniel Duong
@Max 对不起晚回你了。我明白你的意思,但是如果我注释掉那部分代码,我怎么能在输入框触发 keyup 事件时调用数据呢? - Aayush Dahal
@AayushDahal,你需要重新编写整个内容,遵循React的规则...注释掉那部分并不是解决方案,而只是确认重新挂载会导致焦点丢失的一种方式。 - Max
@Max,你有任何资源链接吗?我可以跟随并正确添加列过滤器吗?如果你有这样的资源链接,请分享一个给我好吗? - Aayush Dahal

1

由于input组件失去焦点,这意味着它被重新渲染。只有当一个输入组件的类型已更改或上层树已更改时才会发生。在这种情况下,React会进行完整的重新渲染。

所以我猜测其中一个父组件[ReduxTable,ReduxTableHeader]在每次渲染时都被定义。就像您为TBody所做的那样。

在这种情况下,React认为它是新的组件类型,销毁先前的树并进行完整的重新渲染,这会导致性能延迟,并且在您的情况下使输入失去焦点。

关于性能,您不应该在函数内部定义组件。这会使React变得愚蠢。

为了防止这种情况发生,您需要确保所有输入的父组件都在Render函数之外定义。在这种情况下,在函数组件之外定义。

请检查我的代码沙盒链接,其中演示了2种情况。

  1. 当您尝试编辑输入的第一行时,它不会失去焦点
  2. 当您尝试编辑位于TBody中的其他输入时,将失去焦点。也许您遇到了相同的问题。

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


0
指定一个唯一的键(key)给你的输入组件将会导致React重复使用它而不是重绘它,因此在设置视图状态后,它应该保持焦点。
return (
  <thead>
    <tr>
      /list of header name
    </tr>
    <tr>
      {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={'input' + i}
               />
             ) : (
               ""
             )}
          </th>
      ))}
    </tr>
  </thead>
);

看一下这篇文章


将键添加到输入中并不能解决问题,您能再次查看代码吗?这可能是由于父元素的重新渲染引起的吗?每次输入都会进行API调用,并且整个表格组件可能会因列表更新而重新渲染。 - Aayush Dahal
你可能需要在所有父元素上指定一个关键属性。 - MrCodingB
我已经更新了代码并展示了如何为每个父组件添加键,但问题仍然存在。 - Aayush Dahal
<tr> 也必须有一个键。同样适用于类别和 ReduxTable。 - MrCodingB
我不知道是否还有可以添加键的地方,我已经在我认为必要的每个组件上添加了键。 :( - Aayush Dahal
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接