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

react-data-table-component的DataTable绑定onSelectedRowsChange后复选框失效

问题解决:react-data-table-component复选框选中状态异常

问题根源

你遇到的问题本质是组件重复挂载导致内部状态丢失:

  • 你在InstituteList函数内部定义了PageDataTable等子组件,每次父组件状态更新(比如editButtonDisabled变化),这些子组件会被重新创建,导致DataTable组件卸载后重新挂载,其内部的选中状态被重置。所以第一次点击复选框后,按钮状态更新触发父组件渲染,DataTable重新挂载,复选框回到未选中状态,第二次点击才是新挂载组件的正常选中行为。
  • 另外handleSelectedRowChange使用useCallback但未指定依赖数组,导致每次渲染都会生成新的回调函数,进一步加剧了组件的不必要重渲染。

修复步骤

1. 将内部子组件移到父函数外部

把PageHeader、DataTableHeader、PageDataTable、PageDataTableButtons、PageBody这些组件定义移到InstituteList函数外面,避免每次父组件渲染时重新创建组件:

import { useEffect, useState, useCallback } from 'react';
import { getDocs, query, collection, orderBy, doc } from 'firebase/firestore';
import DataTable from 'react-data-table-component';
import { Button, Checkbox } from '@mui/material';
import { FcInspection } from 'react-icons/fc';
import { IoAdd } from 'react-icons/io5';
import { VscEdit } from 'react-icons/vsc';
import { db } from '../../../services/firebase';
import { Layout } from '../../layout';

// 移到InstituteList外部定义子组件
const PageHeader = (userInfo) => {
  const today = new Date();
  return (
    <div className="page-header">
      <div className="page-header__title">
        <FcInspection />
        조직목록조회
      </div>
    </div>
  )
}

const DataTableHeader = () => {
  return (
    <div className="page-body__datatable_header">
      <div className="page-body__datatable_header__title">조직목록</div>
      <div className="page-body__datatable_header__title"></div>
    </div>
  );
}

const PageDataTable = ({ institutes, handleSelectedRowChange, paginationComponentOptions }) => {
  const columns = [
    { name: 'id', selector: row => row.id, omit: true },
    { name: '조직명', selector: row => row.title, sortable: true, grow: 1 },
    { name: '등록일', selector: row => row.date, sortable: true, grow: 1 },
    { name: '주소', selector: row => row.location, sortable: true, grow: 4 },
  ];
  
  return (
    <div className="page-body__card_datatable">
      <DataTable
        title='조직목록'
        noDataComponent='조회 할 데이터 없습니다.'
        columns={columns}
        data={institutes}
        persistTableHead={true}
        noContextMenu={true}
        selectableRows={true}
        selectableRowsHighlight={true}
        selectableRowsComponent={Checkbox}
        selectableRowsSingle={true}
        onSelectedRowsChange={handleSelectedRowChange}
        pagination
        paginationComponentOptions={paginationComponentOptions}
      />
    </div>
  );
}

const PageDataTableButtons = ({ editButtonDisabled }) => {
  console.log("Render PageDataTableButtons");
  return (
    <div className="page-body__card_buttons">
      <Button variant="contained" id="datatable_button_add" startIcon={<IoAdd />}>추가</Button>
      <Button variant="contained" id="datatable_button_modify" startIcon={<VscEdit />} disabled={editButtonDisabled}>수정</Button>
    </div>
  )
}

const PageBody = ({ institutes, handleSelectedRowChange, paginationComponentOptions, editButtonDisabled }) => {
  return (
    <div className="page-body">
      <div className="page-body__card">
        <PageDataTable 
          institutes={institutes} 
          handleSelectedRowChange={handleSelectedRowChange} 
          paginationComponentOptions={paginationComponentOptions} 
        />
        <PageDataTableButtons editButtonDisabled={editButtonDisabled} />
      </div>
    </div>
  );
}

// 原InstituteList组件
export default function InstituteList() {
  const [retrievedFlag, setRetrievedFlag] = useState(false);
  const [institutes, setInstitutes] = useState([]);
  const [editButtonDisabled, setEditButtonDisabled] = useState(true);

  useEffect(() => {
    if (!retrievedFlag) retrieveData();
  }, [retrievedFlag])

  function retrieveData() {
    try {
      let tmpInstitutes = [];
      const institutesRef = collection(db, 'Institutes');
      const institutesQuery = query(institutesRef, orderBy('title'));
      getDocs(institutesQuery)
      .then(snapshot => {
        snapshot.forEach((doc) => {
          let institute = doc.data();
          institute.id = doc.id;
          tmpInstitutes.push(institute);
        });
        setInstitutes(tmpInstitutes);
        setRetrievedFlag(true);
      });
    } catch (error) {
      console.log(error);
    }
  }

  const handleSelectedRowChange = useCallback((state) => {
    setEditButtonDisabled(state.selectedCount === 0);
  }, []);

  const paginationComponentOptions = {
    rowsPerPageText: '페이지당 조회 갯수',
    rangeSeparatorText: '중',
    selectAllRowsItem: true,
    selectAllRowsItemText: '전체',
  }

  return (
    <Layout>
      {userInfo => (
        <main className="main">
          <PageHeader userInfo={userInfo} />
          <PageBody 
            institutes={institutes} 
            handleSelectedRowChange={handleSelectedRowChange} 
            paginationComponentOptions={paginationComponentOptions} 
            editButtonDisabled={editButtonDisabled} 
          />
        </main>
      )}
    </Layout>
  );
}

2. 修正useEffect和useCallback的依赖

  • 原useEffect的依赖数组为空,补充retrievedFlag作为依赖,符合React Hooks的规范,避免不必要的重复调用。
  • 给handleSelectedRowChange的useCallback加上空依赖数组,确保回调函数的引用稳定,不会每次渲染都生成新函数。

修复原理

  • 将子组件移到父函数外部后,组件的引用不会随父组件渲染而变化,DataTable不会被重复挂载,内部的选中状态得以保留。
  • 稳定的handleSelectedRowChange回调避免了DataTable因回调变化而触发的不必要重渲染,保证了选中状态和按钮状态的同步更新。

内容的提问来源于stack exchange,提问作者Ham Dong Kyun

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 10:45:05