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

Antd Table搜索清空后无法恢复原始数据问题求助

Antd Table搜索过滤后无法恢复原始数据的问题解决

问题描述

我尝试通过Antd Search组件的onChange事件实现Antd Table的数据过滤功能,输入搜索关键词时过滤正常,但清空搜索内容后无法恢复表格的原始数据,疑似状态未在最新onChange事件后更新。

我参考了两个示例:一个是将数据映射为普通元素而非Antd Table的实现,另一个是类组件版本的实现,但难以将其逻辑适配到我的函数组件中,尤其对其中的map函数逻辑存疑。此外,使用原生HTML Table结合map函数的相同实现未出现该问题。

相关代码:

export default function DriversModal({ isVisible, handleClose }) {
  const initialUserData = [
    { type: 2, user: { name: 'TestName1', username: 'name1@email.com'}, phone: '(123) 456-1111', ops: 37 },
    { type: 1, user: { name: 'TestName2', username: 'name2@email.com'}, phone: '(123) 456-2222', ops: 16 },
    { type: 2, user: { name: 'TestName3', username: 'name3@email.com'}, phone: '(123) 456-3333', ops: 4 },
    { type: 1, user: { name: 'TestName4', username: 'name4@email.com'}, phone: '(123) 456-4444', ops: 52 }
  ]

  const columns = [
    {
      title: 'Name',
      dataIndex: 'user',
      render: (user) => (
        <>
          <div className="list-modal-cell-text-primary">{user.name}</div>
          <div className="list-modal-cell-text-secondary">{user.username}</div>
        </>
      ),
      width: 320,
      key: '1',
    },
    {
      title: 'Phone',
      dataIndex: 'phone',
      width: 144,
      key: '2',
    },
    {
      title: 'Ops',
      dataIndex: 'ops',
      key: '3',
    },
  ];

  const [users, setUsers] = useState(initialUserData);
  const [query, setQuery] = useState('');

  const handleChange = (e) => {
    setQuery(e.target.value);
  }

  useEffect(() => {
    setUsers(users.filter(item =>
      item.user.name.toLowerCase().includes(query.toLowerCase()) ||
      item.user.username.toLowerCase().includes(query.toLowerCase())
    ))
  }, [query, users])

  return (
    <Modal
      title={<DriversModalHeader handleChange={handleChange} handleSort={handleSort} query={query} />}
      bodyStyle={{ height: '264px'}}
      visible={isVisible}
      closable={false}
      destroyOnClose={true}
      footer={[
        <Button
          onClick={handleClose}
        >
          Close
        </Button>
      ]}
    >
      <Table
        dataSource={users}
        columns={columns}
        pagination={false}
        scroll={{
          y: 240,
        }}
        row
      />
    </Modal>
  )
}

DriversModalHeader代码:

export default function DriversModalHeader({ handleChange, handleSort, query }) {
  const menu = (
    <Menu>
      <Menu.Item key="availability" onClick={handleSort}>Sort by availability</Menu.Item>
      <Menu.Item key="alphabetic" onClick={handleSort}>Sort alphabetically</Menu.Item>
    </Menu>
  )

  return (
    <div className="list-modal-header">
      <div className="title">
        <div className="title">
          Drivers
        </div>
      </div>
      <div className="elements">
        <div className="list-modal-input-with-button">
          <Input.Search
            value={query}
            placeholder="Search"
            onChange={handleChange}
          />
          <span className="icon">
            <Icon component={Search} style={{fontSize: '16px'}} />
          </span>
        </div>
        <div className="list-modal-dropdown">
          <StateSelect />
        </div>
        <div>
          <Dropdown.Button
            placement="bottomRight"
            className="union"
            overlay={menu}
            trigger={['click']}
            icon={<Icon component={Union} style={{fontSize: '24px'}}/>}
          >
          </Dropdown.Button>
        </div>
      </div>
    </div>
  );
};

问题分析

核心问题是你用过滤后的数据覆盖了原始数据状态:每次useEffect触发时,你基于当前的users状态进行过滤,而users在第一次过滤后已经变成了数据子集。当清空搜索框时,query为空,此时你用已经被过滤过的users执行filter(空条件下会返回当前users),自然无法恢复到最初的完整数据。

另外,useEffect依赖数组中包含users会导致不必要的重复触发,进一步加剧状态混乱。

解决方案

方案1:保留原始数据,每次过滤基于原始数据

将原始数据作为常量保存,每次过滤都从原始数据出发,而非当前的users状态:

export default function DriversModal({ isVisible, handleClose }) {
  // 原始数据作为常量,不修改
  const initialUserData = [
    { type: 2, user: { name: 'TestName1', username: 'name1@email.com'}, phone: '(123) 456-1111', ops: 37 },
    { type: 1, user: { name: 'TestName2', username: 'name2@email.com'}, phone: '(123) 456-2222', ops: 16 },
    { type: 2, user: { name: 'TestName3', username: 'name3@email.com'}, phone: '(123) 456-3333', ops: 4 },
    { type: 1, user: { name: 'TestName4', username: 'name4@email.com'}, phone: '(123) 456-4444', ops: 52 }
  ]

  const columns = [
    {
      title: 'Name',
      dataIndex: 'user',
      render: (user) => (
        <>
          <div className="list-modal-cell-text-primary">{user.name}</div>
          <div className="list-modal-cell-text-secondary">{user.username}</div>
        </>
      ),
      width: 320,
      key: '1',
    },
    {
      title: 'Phone',
      dataIndex: 'phone',
      width: 144,
      key: '2',
    },
    {
      title: 'Ops',
      dataIndex: 'ops',
      key: '3',
    },
  ];

  const [users, setUsers] = useState(initialUserData);
  const [query, setQuery] = useState('');

  const handleChange = (e) => {
    setQuery(e.target.value);
  }

  useEffect(() => {
    // 基于原始数据过滤,而非当前users状态
    const filtered = initialUserData.filter(item =>
      item.user.name.toLowerCase().includes(query.toLowerCase()) ||
      item.user.username.toLowerCase().includes(query.toLowerCase())
    );
    setUsers(filtered);
  }, [query]) // 移除users依赖,避免重复触发

  return (
    <Modal
      title={<DriversModalHeader handleChange={handleChange} query={query} />}
      bodyStyle={{ height: '264px'}}
      visible={isVisible}
      closable={false}
      destroyOnClose={true}
      footer={[
        <Button onClick={handleClose}>Close</Button>
      ]}
    >
      <Table
        dataSource={users}
        columns={columns}
        pagination={false}
        scroll={{ y: 240 }}
      />
    </Modal>
  )
}

方案2:使用衍生状态,直接计算过滤结果(推荐)

不需要将过滤后的数据存入状态,直接在渲染时基于原始数据和query计算结果,避免状态不同步问题:

export default function DriversModal({ isVisible, handleClose }) {
  const initialUserData = [
    { type: 2, user: { name: 'TestName1', username: 'name1@email.com'}, phone: '(123) 456-1111', ops: 37 },
    { type: 1, user: { name: 'TestName2', username: 'name2@email.com'}, phone: '(123) 456-2222', ops: 16 },
    { type: 2, user: { name: 'TestName3', username: 'name3@email.com'}, phone: '(123) 456-3333', ops: 4 },
    { type: 1, user: { name: 'TestName4', username: 'name4@email.com'}, phone: '(123) 456-4444', ops: 52 }
  ]

  const columns = [
    {
      title: 'Name',
      dataIndex: 'user',
      render: (user) => (
        <>
          <div className="list-modal-cell-text-primary">{user.name}</div>
          <div className="list-modal-cell-text-secondary">{user.username}</div>
        </>
      ),
      width: 320,
      key: '1',
    },
    {
      title: 'Phone',
      dataIndex: 'phone',
      width: 144,
      key: '2',
    },
    {
      title: 'Ops',
      dataIndex: 'ops',
      key: '3',
    },
  ];

  const [query, setQuery] = useState('');

  const handleChange = (e) => {
    setQuery(e.target.value);
  }

  // 直接计算过滤结果,无需存入状态
  const filteredUsers = initialUserData.filter(item =>
    item.user.name.toLowerCase().includes(query.toLowerCase()) ||
    item.user.username.toLowerCase().includes(query.toLowerCase())
  );

  return (
    <Modal
      title={<DriversModalHeader handleChange={handleChange} query={query} />}
      bodyStyle={{ height: '264px'}}
      visible={isVisible}
      closable={false}
      destroyOnClose={true}
      footer={[
        <Button onClick={handleClose}>Close</Button>
      ]}
    >
      <Table
        dataSource={filteredUsers}
        columns={columns}
        pagination={false}
        scroll={{ y: 240 }}
      />
    </Modal>
  )
}

额外说明

  • 方案2更简洁,减少了不必要的状态管理,完全避免状态不同步风险。
  • 注意移除原代码中Table组件多余的row属性,避免控制台报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 21:45:36