You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mantine DataTable中TextInput失焦问题及表格过滤实现求助

解决Mantine DataTable中TextInput按键失焦问题

问题根源

你在Customers组件内部定义了NameColumnHeader组件,每次输入框触发filter状态更新时,Customers组件会重新渲染,NameColumnHeader会被重新创建为全新的组件实例。DataTable检测到表头元素变化后,会卸载原输入框并重建,直接导致输入框失焦。

另外你的数据获取逻辑存在无限循环问题:useEffect依赖customersNotEmpty,获取数据后设置该状态为true,会再次触发这个useEffect重复请求数据。

修复代码

import React, { useState, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { Link } from 'react-router-dom';
import { setPageTitle } from '../store/themeConfigSlice';
import { getCustomers } from '../services/customerService';
import { DataTable, ActionIcon, TextInput } from 'mantine-datatable';
import { IconX } from '@tabler/icons-react';

// 将表头组件移到外部,通过props传递状态
const NameColumnHeader = ({ filter, setFilter }: { filter: string; setFilter: (value: string) => void }) => (
  <div className="flex items-center justify-between">
    <span className="mr-2">Name</span>
    <TextInput
      label=""
      placeholder="Search by Name..."
      value={filter}
      onChange={(e) => setFilter(e.currentTarget.value)}
      rightSection={
        filter && (
          <ActionIcon
            size="sm"
            variant="transparent"
            c="dimmed"
            onClick={() => setFilter('')}
          >
            <IconX size={14} />
          </ActionIcon>
        )
      }
    />
  </div>
);

const Customers = (): JSX.Element => {
  const dispatch = useDispatch();

  const [customers, setCustomers] = useState([] as any);
  const [filter, setFilter] = useState('');

  useEffect(() => {
    dispatch(setPageTitle('Customers'));
  }, []);

  // 修正数据获取逻辑:仅在组件挂载时请求一次
  useEffect(() => {
    getCustomers()
      .then((response) => setCustomers(response.data));
  }, []);

  const filteredData = customers?.filter((item: any) =>
    item.name.toLowerCase().includes(filter.toLowerCase())
  );

  return (
    <div>
      <ul className="flex space-x-2 rtl:space-x-reverse">
        <li>
          <Link to="/" className="text-primary hover:underline">
            Dashboard
          </Link>
        </li>
        <li className="before:content-['/'] ltr:before:mr-2 rtl:before:ml-2">
          <span>Customers</span>
        </li>
      </ul>
      <div className="flex flex-col xl:flex-row gap-5 relative mt-5">
        <DataTable
          noRecordsText="No results match your search query"
          highlightOnHover
          className="whitespace-nowrap table-hover"
          records={filteredData}
          columns={[
            { accessor: 'client_id', title: '#' },
            // 传递状态给外部组件
            { accessor: 'name', title: <NameColumnHeader filter={filter} setFilter={setFilter} /> },
            { accessor: 'code', title: 'Code' },
            { accessor: 'address_city', title: 'City' },
            { accessor: 'address_province', title: 'Province' },
            { accessor: 'country', title: 'Country' },
            { accessor: 'vat_number', title: 'VAT' },
            { accessor: 'id', title: 'ID' },
          ]}
          totalRecords={customers?.length}
          recordsPerPage={10}
          page={1}
          onPageChange={() => null}
          recordsPerPageOptions={[0]}
          onRecordsPerPageChange={() => null}
          sortStatus={undefined}
          onSortStatusChange={undefined}
          minHeight={200}
          paginationText={({ from, to, totalRecords }) =>
            `Showing  ${from} to ${to} of ${totalRecords} entries`
          }
        />
      </div>
    </div>
  );
};

export default Customers;

关键修改点

  1. 提取表头组件:把NameColumnHeader移到Customers组件外部,避免每次渲染时创建新组件实例,保证输入框的DOM稳定性。
  2. 修正数据请求逻辑:移除customersNotEmpty状态和对应的依赖,改为仅在组件挂载时请求一次数据,解决无限循环问题。
  3. 传递状态props:通过props将filter和setFilter传递给NameColumnHeader,保持状态控制的正确性。

内容的提问来源于stack exchange,提问作者user74747

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 06:55:19