使用react-d3-tree+TypeScript时nodeSvgShape等属性不被识别
react-d3-tree属性无法识别、TS报错/JSX无法渲染问题解决
问题详情
- 使用react-d3-tree的
<Tree>组件时,nodeSvgShape、nodeLabelComponent、allowForeignObjects等官方文档标注的属性无法被TypeScript识别,触发类型错误 - 已确认所有依赖为最新版本,尝试过切换不同版本的react-d3-tree,问题未解决
- 将文件后缀从
.tsx改为.jsx后,控制台类型错误消失,但Tree组件完全无法渲染
提供的代码(TreeCanvas.tsx)
/* eslint-disable react-hooks/exhaustive-deps */ import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; import Tree from 'react-d3-tree'; import './Nodes.css'; const treeData1 = { name: 'Family Title', className: 'node', children: [ { name: 'Child 1', className: 'node', children: [ { name: 'Grandchild 1', className: 'node', }, { name: 'Grandchild 2', className: 'node', }, ], }, { name: 'Child 2', className: 'node', children: [ { name: 'Grandchild 3', className: 'node', }, { name: 'Grandchild 4', className: 'node', }, ], }, ], }; const path = { pathFunc: (linkDatum: any, orientation: any) => { const { source, target } = linkDatum; // eslint-disable-next-line @typescript-eslint/no-unused-vars const x = orientation === 'vertical' ? source.x : source.y; const y = orientation === 'vertical' ? target.y : target.x; const deltaX = Math.abs(source.x - target.x); const deltaY = Math.abs(source.y - target.y); const radius = Math.min(deltaX, deltaY) / 2; const offsetY = y > source.y ? radius : -radius; return `M${source.x},${source.y} V${y + offsetY} H${target.x}`; }, }; const TreeCanvas = () => { const ref = useRef<HTMLDivElement>(null); // set the type of ref to HTMLDivElement // eslint-disable-next-line @typescript-eslint/no-unused-vars const [numbers, setNumbers] = useState([]); const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); useLayoutEffect(() => { if (ref.current) { // make sure that ref.current is not null before accessing clientWidth and clientHeight setWidth(ref.current.clientWidth); setHeight(ref.current.clientHeight); } }, [numbers, ref.current]); useEffect(() => { function handleWindowResize() { if (ref.current) { // make sure that ref.current is not null before accessing clientWidth and clientHeight setWidth(ref.current.clientWidth); setHeight(ref.current.clientHeight); } } window.addEventListener('resize', handleWindowResize); return () => { window.removeEventListener('resize', handleWindowResize); }; }, [ref.current]); const svgSquare = { shape: 'rect', shapeProps: { width: 20, height: 20, x: -10, y: -10, } } const MyTree = ({ data }: any) => ( <Tree data={data} orientation="vertical" translate={{ x: width / 2.35, y: height / 4 }} pathFunc={path.pathFunc} nodeSvgShape={svgSquare} /> ); return ( <div id="treeCanvas" ref={ref}> <MyTree data={treeData1} /> </div> ); }; export default TreeCanvas;
解决方案
1. 修复TypeScript属性识别问题
原因
react-d3-tree的官方类型定义包@types/react-d3-tree可能未同步更新所有属性,导致TypeScript无法识别这些配置项。
解决方法
- 安装匹配版本的类型包:确保
@types/react-d3-tree版本与react-d3-tree主包版本一致,执行命令:
npm install @types/react-d3-tree@<你的react-d3-tree版本号> --save-dev
- 扩展类型接口:如果类型包确实缺失属性,可以手动扩展
TreeProps接口,在项目中添加以下代码:
// 可以放在TreeCanvas.tsx顶部,或者单独的类型声明文件中 import { TreeProps } from 'react-d3-tree'; declare module 'react-d3-tree' { interface TreeProps { nodeSvgShape?: { shape: string; shapeProps: Record<string, any>; }; nodeLabelComponent?: React.FC<any>; allowForeignObjects?: boolean; } }
- 临时忽略类型错误:如果需要快速验证功能,可以在属性前添加
// @ts-ignore注释(不推荐长期使用):
<Tree data={data} orientation="vertical" translate={{ x: width / 2.35, y: height / 4 }} pathFunc={path.pathFunc} // @ts-ignore nodeSvgShape={svgSquare} />
2. 修复JSX下组件无法渲染问题
原因
组件初始化时width和height为0,Tree组件没有可用的渲染尺寸,导致无法生成SVG内容。同时,依赖项中直接使用ref.current会导致React钩子依赖判断异常,尺寸更新逻辑失效。
解决方法
- 给容器设置默认尺寸:在
Nodes.css中添加容器样式,确保初始有可用尺寸:
#treeCanvas { width: 100%; height: 100vh; min-width: 800px; min-height: 600px; }
- 优化尺寸状态初始化:避免初始值为0,直接获取容器初始尺寸或设置默认值:
const TreeCanvas = () => { const ref = useRef<HTMLDivElement>(null); // 初始化时设置默认尺寸,确保Tree有渲染空间 const [width, setWidth] = useState(ref.current?.clientWidth || 800); const [height, setHeight] = useState(ref.current?.clientHeight || 600); // ...其余代码 };
- 修正钩子依赖项:将
ref.current替换为ref作为依赖(ref本身是稳定引用,ref.current变化不会触发依赖更新):
useLayoutEffect(() => { const updateSize = () => { if (ref.current) { setWidth(ref.current.clientWidth); setHeight(ref.current.clientHeight); } }; updateSize(); }, [numbers, ref]); // 用ref代替ref.current useEffect(() => { function handleWindowResize() { if (ref.current) { setWidth(ref.current.clientWidth); setHeight(ref.current.clientHeight); } } window.addEventListener('resize', handleWindowResize); return () => window.removeEventListener('resize', handleWindowResize); }, [ref]); // 用ref代替ref.current
- 添加渲染时机判断:确保尺寸有效时再渲染Tree组件:
const MyTree = ({ data }: any) => { // 尺寸为0时不渲染,避免无效渲染 if (width === 0 || height === 0) return null; return ( <Tree data={data} orientation="vertical" translate={{ x: width / 2.35, y: height / 4 }} pathFunc={path.pathFunc} nodeSvgShape={svgSquare} /> ); };
内容的提问来源于stack exchange,提问作者gryxay
相关产品推荐
相关产品推荐

