如何在Tabulator单元格格式化器中合理渲染React组件
在Tabulator自定义格式化器中使用React组件的优化方案
问题背景
已实现通过自定义格式化器在Tabulator表格列中渲染React组件,但存在资源未清理、性能开销大的问题,相关代码如下:
示例React组件
type ReactThingProps = { children: any; colour: string; }; const ReactThing: React.FunctionComponent<ReactThingProps> = ({ colour, children, }: ReactThingProps) => { return <span style={{ backgroundColor: colour }}>{children}</span>; }; export default ReactThing;
当前自定义格式化器实现
const ReactFormatter = ( cell: any, _formatterParams: object, _onRendered: EmptyCallback ) => { const reactNode = document.createElement("span"); const reactRoot = createRoot(reactNode); reactRoot.render( <ReactThing colour={cell.getValue()}>{cell.getValue()}</ReactThing> ); return reactNode; };
列配置方式
{ title: "Despised Color", field: "despCol", formatter: ReactFormatter },
问题
- 表格采用虚拟DOM机制,单元格可能被销毁,需监听哪个事件执行React根的
unmount清理? - 频繁创建、销毁React根有性能损耗,有没有更优的方式在格式化器中引入React组件?
解答
问题1:React根的清理时机
Tabulator的cellDestroyed事件会在单元格被销毁(包括虚拟滚动时移出视口)时触发,可在格式化器中为单元格绑定该事件完成清理:
const ReactFormatter = ( cell: any, _formatterParams: object, _onRendered: EmptyCallback ) => { const reactNode = document.createElement("span"); const reactRoot = createRoot(reactNode); reactRoot.render( <ReactThing colour={cell.getValue()}>{cell.getValue()}</ReactThing> ); // 单元格销毁时卸载React根 cell.on("cellDestroyed", () => { reactRoot.unmount(); }); return reactNode; };
问题2:性能优化方案
推荐两种方案避免频繁创建React根:
方案1:使用react-tabulator封装库
直接使用官方适配的react-tabulator库,它原生支持在列定义中传入React组件,自动处理组件的挂载、更新与卸载,完全适配虚拟DOM:
{ title: "Despised Color", field: "despCol", cellComponent: (props) => ( <ReactThing colour={props.cell.getValue()}>{props.cell.getValue()}</ReactThing> ) }
方案2:复用React根节点
如果不想引入第三方库,可在单元格DOM上存储已创建的React根实例,数据更新时仅更新组件props,而非重建根:
const ReactFormatter = ( cell: any, _formatterParams: object, _onRendered: EmptyCallback ) => { let reactNode = cell.getElement().querySelector(".react-cell-container"); let reactRoot; if (!reactNode) { // 首次渲染:创建容器与根实例 reactNode = document.createElement("span"); reactNode.className = "react-cell-container"; reactRoot = createRoot(reactNode); // 存储根实例到DOM节点 (reactNode as any).reactRootInstance = reactRoot; // 绑定销毁事件 cell.on("cellDestroyed", () => { reactRoot.unmount(); }); } else { // 复用已有根实例 reactRoot = (reactNode as any).reactRootInstance; } // 渲染/更新组件 reactRoot.render( <ReactThing colour={cell.getValue()}>{cell.getValue()}</ReactThing> ); return reactNode; };
这种方式仅在首次渲染时创建React根,后续仅更新组件props,大幅降低性能开销。
内容的提问来源于stack exchange,提问作者Notre
相关产品推荐
相关产品推荐

