如何避免React/Redux Datatables在数据准备完成前提前初始化
问题解决办法
你遇到的问题本质是异步请求时序和Datatable初始化逻辑不匹配导致的。组件挂载后先触发Redux的异步拉数请求,此时Datatable检测到表格DOM已经渲染完成,会直接用空的初始数据完成初始化,显示「无可用数据」提示。等Redux拉取到数据更新props后,React只会重渲染tbody里的行元素,Datatable本身不会感知到这部分DOM更新,因此你会看到新渲染的行出现在原有提示的下方。
以下是两种可直接落地的解决方案:
方案1:延迟渲染表格,等数据加载完成后再初始化Datatable
这是最稳妥的方案,不会出现空白或者重复初始化的问题。
操作步骤:
- 先调整Redux中readiness模块的state结构,新增loading状态标识请求状态,标准异步state结构参考:
// readiness reducer 初始值 const initialState = { data: [], // 原来的readiness数组存在这 loading: false, error: null } // 请求发起时的reducer逻辑 case 'GET_READINESS_REQUEST': return {...state, loading: true } // 请求成功时的reducer逻辑 case 'GET_READINESS_SUCCESS': return {...state, loading: false, data: action.payload }
- 调整组件的mapStateToProps,拿到loading状态,只有数据加载完成后才渲染完整表格结构:
import React, { Component, Fragment } from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { getReadiness } from '../../actions/readiness'; // 引入你用的Datatable依赖 import $ from 'jquery'; import 'datatables.net'; export class Readiness extends Component { static propTypes = { readiness: PropTypes.array.isRequired, loading: PropTypes.bool.isRequired, getReadiness: PropTypes.func.isRequired }; componentDidMount() { this.props.getReadiness(); } componentDidUpdate(prevProps) { // 第一次加载完成,loading从true变false的时候初始化Datatable if (prevProps.loading && !this.props.loading) { $('#your-table-id').DataTable({ // 你的Datatable配置项 }); } } componentWillUnmount() { // 组件卸载时销毁实例,避免内存泄漏 if ($.fn.DataTable.isDataTable('#your-table-id')) { $('#your-table-id').DataTable().destroy(); } } render() { const { loading, readiness } = this.props; return ( <Fragment> <section id="compact-style"> <div class="row"> {/* 加载中显示提示,加载完成再渲染表格 */} {loading ? ( <p>数据加载中...</p> ) : ( <table id="your-table-id"> <thead> {/* 你的表头代码 */} </thead> <tbody> {readiness.map(item => ( <tr key={item.id}> {/* 你的单元格代码 */} </tr> ))} </tbody> </table> )} </div> </section> </Fragment> ); } } const mapStateToProps = state => ({ readiness: state.readiness.data, loading: state.readiness.loading }) export default connect(mapStateToProps, { getReadiness })(Readiness);
方案2:保留现有结构,数据更新后手动刷新Datatable
如果你不想修改现有Redux的state结构,可以用这个方案:
// 其他代码和你原有代码一致,只需要新增生命周期和实例存储 export class Readiness extends Component { tableInstance = null; componentDidMount() { this.props.getReadiness(); // 先初始化Datatable,存下实例 this.tableInstance = $('#your-table-id').DataTable({ // 你的配置项 }); } componentDidUpdate(prevProps) { // 检测到数据变化,更新Datatable数据 if (prevProps.readiness !== this.props.readiness) { // 清空原有数据,插入新数据后重绘 this.tableInstance.clear().rows.add(this.props.readiness).draw(); } } componentWillUnmount() { this.tableInstance?.destroy(); } // 其余render代码和你原有代码一致,注意tbody里不需要自己写map渲染行,Datatable会自动根据传入的数据渲染 render() { return ( <Fragment> <section id="compact-style"> <div class="row"> <table id="your-table-id"> <thead> {/* 表头代码 */} </thead> <tbody> {/* 这里不用自己写map,Datatable会处理 */} </tbody> </table> </div> </section> </Fragment> ) } }
注意事项
- 不要同时用React渲染行和Datatable渲染行,两种方案选一种就行,不然会出现重复数据
- 记得组件卸载时销毁Datatable实例,避免页面切换后出现内存泄漏问题
- 如果是用第三方封装的React版Datatable组件,可以直接把readiness数组传给组件的data属性,组件内部会自动处理更新逻辑,不需要手动操作实例
内容的提问来源于stack exchange,提问作者Justin Boucher
相关产品推荐
相关产品推荐

