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

React useState Hook删除元素时数组未更新的问题排查

React表格删除行后状态异常:已删行重复出现的问题

我有一个React表格,其中一列包含操作按钮,可通过removeRow(idx)回调删除对应行。该处理程序能正确筛选移除目标行,但后续尝试删除更多行时,已删除的行会重新出现。通过控制台日志可见,尽管表格显示已移除一行(最多一行),但React并未更新rowData状态,每次console.log('rowData', rowData)都输出原始数组。我曾尝试将初始状态设为空数组,但修改后点击removeRow(idx)函数会直接渲染空表格。请问这是什么原因?

UsersTable.js

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';

import { MDBDatatable, MDBBtn, MDBIcon } from 'mdbReactUiKit';
import Wrapper from '../Wrapper';
import styled from 'styled-components';

import { rows, cols } from '../../variables/Users';

...

export default function UsersTable() {
  const [colData, setColData] = useState(cols)
  const [rowData, setRowData] = useState(addCustomButtons(rows))
  
  function addCustomButtons(rows) {
    return rows.map((row, idx) => {
      return {
        ...row,
        index: idx,
        email: (
          <StyledLink
            to='#'
            onClick={(e) => {
              window.location.href = `mailto:${row.emailraw}`;
              e.preventDefault();
            }}
          >
            {row.emailraw}
          </StyledLink>
        ),
        buttons: (
          <>
            <StyledButton
              size='sm'
              floating
              className='message-btn'
              onClick={() => console.log(`send a message to ${row.emailraw}`)}
            >
              <MDBIcon icon='envelope' />
            </StyledButton>
            <StyledButton outline size='sm' floating className='call-btn' onClick={() => console.log(`edit user settings`)}>
              <MDBIcon icon='ellipsis-h' />
            </StyledButton>
            <StyledTrash
              size='sm'
              floating
              className='remove-user-btn'
              onClick={() => removeRow(idx)}
            >
              <MDBIcon icon="trash" />
            </StyledTrash>
          </>
        ),
      };
    })
  }

// does not work
  const deleteRow = (index) => {
    if (rowData.length > 0) {
      var updatedRows = [...rowData]
      console.log('1st rowData', updatedRows)

      var indexToRemove = updatedRows.findIndex(x => x.index === index)
      console.log('remove', indexToRemove, index)

      if (indexToRemove > -1) {
        updatedRows.splice(indexToRemove, 1)
        var newRows = addCustomButtons(updatedRows)

        console.log(newRows)

        setRowData(newRows)
        // setTableData((prevState) => {
        //   return {
        //     ...prevState,
        //     rows: newRows
        //   }
        // })
      }
    }
  }

// almost works
  const removeRow = (index) => {
    console.log('rowData', rowData)
    const oldRows = [...rowData]
    const updatedRows = oldRows.filter((row) => row.index !== index);
    console.log('updated', updatedRows)

    setRowData(updatedRows)

    setTimeout(() => {
      console.log('timer', rowData)
    }, 3000)
  }


  const Table = ({ colData, rowData }) => {
    let tableData = {
      columns: colData,
      rows: rowData
    }

    useEffect(() => {
      tableData = {
        columns: colData,
        rows: rowData
      }
    }, [rowData])

  
    return (
      <StyledTable
        hover
        striped
  
        data={tableData}
        entriesOptions={[5, 10, 20]}
        entries={10}
  
        fixedHeader
        maxHeight='460px'
      />
    )
  }

  return (
    <Wrapper>
      <TableContainer>
        <Table colData={colData} rowData={rowData} />
      </TableContainer>
    </Wrapper>
  );
}

问题根源与解决方案

1. 闭包导致旧索引绑定

初始调用addCustomButtons(rows)生成行数据时,删除按钮的removeRow(idx)绑定的是原始数组的循环索引。删除一行后,rowData更新,但新行没有重新生成按钮并绑定当前正确的索引,后续删除时,row.index !== index中的index仍是旧索引,无法匹配新行的索引值,导致筛选失效、已删行“重现”。

2. 状态更新未用函数式更新

removeRow中直接依赖当前rowData生成新数组,由于React状态更新是异步的,连续操作时可能获取到旧的rowData值,导致状态更新错误,日志始终输出原始数组。

3. 子组件Table的无效useEffect逻辑

子组件中先声明let tableData再通过useEffect赋值是错误的——组件渲染时会重新初始化tableData,useEffect的赋值不会影响当前渲染,导致表格无法响应rowData的变化。


修正后的关键代码

修正removeRow函数(函数式更新+重新生成按钮)

const removeRow = (index) => {
  // 函数式更新确保基于最新状态计算新值
  setRowData(prevRowData => {
    const filteredRows = prevRowData.filter(row => row.index !== index);
    // 重新生成按钮并更新行索引
    return addCustomButtons(filteredRows);
  });
}

修正子组件Table

const Table = ({ colData, rowData }) => {
  // 直接基于props生成tableData,无需useEffect
  const tableData = {
    columns: colData,
    rows: rowData
  };

  return (
    <StyledTable
      hover
      striped
      data={tableData}
      entriesOptions={[5, 10, 20]}
      entries={10}
      fixedHeader
      maxHeight='460px'
    />
  );
}

初始状态设为空数组的问题说明

如果初始rows为空数组,调用addCustomButtons(rows)会生成空的rowData,因此表格直接渲染为空。若需支持初始空状态,可在addCustomButtons中添加空数组判断,或者初始状态设为[],后续通过接口请求后再调用addCustomButtons更新rowData。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 03:16:04