React集成DataTable更新时触发removeChild错误,求解决方案
React中DataTable更新时出现removeChild错误的解决方案
问题描述
在React项目中使用DataTable,每次执行更新操作时都会抛出错误:Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node,且必须使用该DataTable。原代码如下:
const columns = [ { title: "Category", data: "category" }, { title: "Name", data: "name" }, { title: "Subscribers", data: "subscribers" }, { title: "Total News Received", data: "total_news_received" }, ]; const Table = ({ selectionRange }) => { const tableRef = useRef(); const dispatch = useDispatch(); const { loading_no_subscribers, data, news_stats_error, } = useSelector((state) => state.GetNoSubscribersReducer); useEffect(() => { dispatch(GetNoSubscribersAction({ selectionRange })); }, [dispatch, selectionRange]); useEffect(() => { if (data && data.result) { const tableData = [ ...Object.entries(data?.result?.total_subscribers_service_providers_count).map(([key, value]) => ({ category: "Service Providers", name: key.replace('_', ' '), subscribers: value, total_news_received: data?.result?.total_news_recieved_service_providers[key] || 0, })), ...Object.entries(data?.ind).map(([key, value]) => ({ category: "Industry", name: key subscribers: value, total_news_received: data?.ind[key] || 0, })), ...Object.entries(data?.tt).map(([key, value]) => ({ category: "Avasant Subscribers", name: key.replace(/^\d+_/, '').replace('_', ' '), subscribers: value, total_news_received: data?.tt[key] || 0, })), ]; if ($.fn.DataTable.isDataTable(tableRef.current)) { $(tableRef.current).DataTable().clear().destroy(); $(tableRef.current).find('tbody').empty(); // Clear the table body } $(tableRef.current).DataTable({ data: tableData, columns: columns, }); } }, [data]); return ( <section> <h2 style={{ color: '#136ebd' }}>Subscribers Statistics</h2> {loading_no_subscribers ? ( <Loader /> ) : news_stats_error ? ( <p className="text-sm text-red-800">Something went wrong: {news_stats_error}</p> ) : ( <table ref={tableRef} className="display min-w-full divide-y divide-gray-200 dark:divide-neutral-700 border border-gray-100 dark:border-neutral-300" style={{ borderRadius: '7px' }} > <thead> <tr style={{ backgroundColor: '#136ebd' }}> <th className="px-6 py-3 text-start text-xs font-medium text-gray-100 uppercase dark:text-neutral-500">Category</th> <th className="px-6 py-3 text-start text-xs font-medium text-gray-100 uppercase dark:text-neutral-500">Sub Category</th> <th className="px-6 py-3 text-start text-xs font-medium text-gray-100 uppercase dark:text-neutral-500">Subscribers</th> <th className="px-6 py-3 text-start text-xs font-medium text-gray-100 uppercase dark:text-neutral-500">Total News Received</th> </tr> </thead> <tbody> {/* DataTables will populate rows here */} </tbody> </table> )} </section> ); }; export default Table;
原因分析
- 语法错误:处理
data?.ind的map函数中,name: key后缺少逗号,导致数据生成失败,间接引发DOM操作异常。 - 虚拟DOM与原生DOM冲突:手动渲染的
<thead>会和DataTables自动生成的表头重复,当DataTables更新DOM时,React虚拟DOM管理与原生DOM操作出现不一致,导致移除节点时找不到对应子节点。 - 实例销毁不彻底:重新初始化前手动清空tbody的操作干扰了DataTables的内部DOM管理,且未在组件卸载时清理实例,引发内存泄漏和DOM残留。
解决方案
- 修复语法错误,确保数据正确生成;
- 移除手动渲染的thead,让DataTables自动生成表头,避免DOM结构冲突;
- 在useEffect的清理函数中销毁DataTable实例,优化实例生命周期管理。
修复后的完整代码
const columns = [ { title: "Category", data: "category" }, { title: "Name", data: "name" }, { title: "Subscribers", data: "subscribers" }, { title: "Total News Received", data: "total_news_received" }, ]; const Table = ({ selectionRange }) => { const tableRef = useRef(); const dispatch = useDispatch(); const { loading_no_subscribers, data, news_stats_error, } = useSelector((state) => state.GetNoSubscribersReducer); useEffect(() => { dispatch(GetNoSubscribersAction({ selectionRange })); }, [dispatch, selectionRange]); useEffect(() => { let tableInstance = null; if (data && data.result) { // 修复语法错误:补全逗号 const tableData = [ ...Object.entries(data?.result?.total_subscribers_service_providers_count).map(([key, value]) => ({ category: "Service Providers", name: key.replace('_', ' '), subscribers: value, total_news_received: data?.result?.total_news_recieved_service_providers[key] || 0, })), ...Object.entries(data?.ind).map(([key, value]) => ({ category: "Industry", name: key, // 补全缺失的逗号 subscribers: value, total_news_received: data?.ind[key] || 0, })), ...Object.entries(data?.tt).map(([key, value]) => ({ category: "Avasant Subscribers", name: key.replace(/^\d+_/, '').replace('_', ' '), subscribers: value, total_news_received: data?.tt[key] || 0, })), ]; // 销毁旧实例 if ($.fn.DataTable.isDataTable(tableRef.current)) { $(tableRef.current).DataTable().destroy(); } // 初始化新实例,配置表头样式 tableInstance = $(tableRef.current).DataTable({ data: tableData, columns: columns, initComplete: function() { // 手动设置表头样式,替代之前的手动thead $(this.api().table().header()).css({ backgroundColor: '#136ebd' }); $(this.api().table().header()).find('th').addClass('px-6 py-3 text-start text-xs font-medium text-gray-100 uppercase dark:text-neutral-500'); } }); } // 组件卸载或data更新前销毁实例 return () => { if (tableInstance) { tableInstance.destroy(); } }; }, [data]); return ( <section> <h2 style={{ color: '#136ebd' }}>Subscribers Statistics</h2> {loading_no_subscribers ? ( <Loader /> ) : news_stats_error ? ( <p className="text-sm text-red-800">Something went wrong: {news_stats_error}</p> ) : ( // 移除手动渲染的thead,由DataTables自动生成 <table ref={tableRef} className="display min-w-full divide-y divide-gray-200 dark:divide-neutral-700 border border-gray-100 dark:border-neutral-300" style={{ borderRadius: '7px' }} > <tbody></tbody> </table> )} </section> ); }; export default Table;
内容的提问来源于stack exchange,提问作者Awanish Mishra
相关产品推荐
相关产品推荐

