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
相关产品推荐
相关产品推荐

