原创 - react + TS + d3.js 实现曲线图 报错问题解决
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了原创 - react + TS + d3.js 实现曲线图 报错问题解决,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6755字,纯文字阅读大概需要10分钟。
内容图文
![原创 - react + TS + d3.js 实现曲线图 报错问题解决](/upload/InfoBanner/zyjiaocheng/1303/e379edad688a4028b38d3c0319f46d98.jpg)
react + TS + d3.js 实现曲线图 报错问题解决
1. 结构
1.1 整体结构
// lemon
import React, { useRef, useState, useEffect, useReducer } from "react";
import * as d3 from "d3";
interface Data {}
const SimpleLine: React.FC = () => {
const ref = useRef<SVGSVGElement>(null);
useEffect(() => {});
return (
<>
<svg ref={ref}></svg>
</>
);
};
export { SimpleLine };
1.2 SimpleLine函数组件
在SimpleLine组件中的svgDOM结构中进行SVG图表的绘制。由于绘制SVG图表中会产生副作用(直接操作DOM结构),所以这部分操作将在Effect Hook中执行。
我们想使用d3.js在svg标签中绘制数据图表,所以需要先获取到组件渲染后的真实DOM。这里使用ref来访问。
在TS环境下使用useRef()时,需要使用泛型声明需要保存的DOM类型。这里想要保存svg,对应的泛型为SVGSVGElement。可能不完全正确。
1.3 interface Data{}
这个接口用于声明在d3.js中需要操作的数据元(datum),可视化的数据集由许许多多的datum组成。
使用d3.js可视化的操作中,我们往往使用的是datum中的某个数据,使用Data来标识datum的类型(属性)可以避免误操作、报错、无法获取属性。
2. 代码
2.1 完整代码
import React, { useRef, useEffect, useState } from "react";
import * as d3 from "d3";
interface IData {
date: Date;
value: number;
}
const SimpleLine: React.FC = () => {
const ref = useRef<SVGSVGElement>(null);
const [width, setWidth] = useState(1600);
const [height, setHeight] = useState(800);
const [margin, setMargin] = useState({
top: 160,
right: 80,
bottom: 160,
left: 80,
});
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
useEffect(() => {
const svgSelection = d3.select(ref.current);
var data: IData[] = [
{ date: new Date(2007, 3, 24), value: 93.24 },
{ date: new Date(2007, 3, 25), value: 95.35 },
{ date: new Date(2007, 3, 26), value: 98.84 },
{ date: new Date(2007, 3, 27), value: 99.92 },
{ date: new Date(2007, 3, 30), value: 99.8 },
{ date: new Date(2007, 4, 1), value: 99.47 },
];
data.forEach((d) => {
console.log(d.date);
});
const xValue = (d: IData): Date => d.date;
const yValue = (d: IData): number => d.value;
let xScale: d3.ScaleTime<number, number>,
yScale: d3.ScaleLinear<number, number>;
let datesKeys: Date[];
const g = svgSelection
.append("g")
.attr("id", "maingroup")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
const init = (data: IData[]) => {
svgSelection.attr("width", width).attr("height", height);
let minX = d3.min(data, xValue) || new Date();
let maxX = d3.max(data, xValue) || new Date();
let minY = d3.min(data, yValue) || 0;
let maxY = d3.max(data, yValue) || 0;
xScale = d3
.scaleTime()
.domain([minX, maxX])
.range([0, innerWidth])
.nice();
yScale = d3
.scaleLinear()
.domain([maxY, minY])
.range([0, innerHeight])
.nice();
datesKeys = Array.from(new Set(data.map((d) => d.date)));
// Adding axes
const xAxis = d3
.axisBottom(xScale)
.tickValues(Array.from(new Set(data.map((e) => e.date))))
.tickSize(-innerHeight);
const yAxis = d3.axisLeft(yScale).tickSize(-innerWidth);
const xAxisGroup = g
.append("g")
.call(xAxis)
.attr("transform", `translate(0, ${innerHeight})`);
const yAxisGroup = g.append("g").call(yAxis);
g.selectAll(".tick text").attr("font-size", "1em");
g.append("path").attr("id", "alterPath");
};
const renderUpdate = (data: IData[]) => {
const line = d3
.line<IData>()
.x((d: IData) => {
return xScale(xValue(d)) || 0;
})
.y((d: IData) => {
return yScale(yValue(d)) || 0;
})
.curve(d3.curveCardinal.tension(0.5));
// lineEmpty is typically used for the first animation that raise the line up;
const lineEmpty = d3
.line<IData>()
.x((d: IData) => {
return xScale(xValue(d)) || 0;
})
.y((d: IData) => {
return yScale(d3.min(data, yValue) || 0) || 0;
})
.curve(d3.curveCardinal.tension(0.5));
// .curve(d3.curveCardinal.tension(0.5));
const maingroup = d3.select("#maingroup");
// maingroup
// .append("path")
// .attr("d", line(data) || "")
// .attr("stroke", "black")
// .attr("fill", "none");
const pathUpdate = maingroup.selectAll(".datacurve").data(data);
const pathEnter = pathUpdate
.enter()
.append("path")
.attr("class", "datacurve")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2.5)
.attr("d", line(data) || "");
pathUpdate
.merge(d3.selectAll(".datacurve"))
.transition()
.duration(2000)
.ease(d3.easeLinear)
.attr("d", lineEmpty(data) || "");
console.log(line(data));
console.log(lineEmpty(data));
};
init(data);
renderUpdate(data);
});
return (
<>
<svg ref={ref}></svg>
</>
);
};
export { SimpleLine };
2.2 报错解决
2.2.1 domain()
let yScale = d3.scaleLinear().domain([d3. min(data, yValue), d3.max(data, yValue)]).range([0, innerHeight]).nice();
报错
Type ‘number | undefined‘ is not assignable to type ‘NumberValue‘.
Type ‘undefined‘ is not assignable to type ‘NumberValue‘.ts(2322)
说参数有可能是undefined,不合理。
let minY = d3.min(data, yValue) || 0;
let maxY = d3.max(data, yValue) || 0;
let yScale = d3.scaleLinear().domain([minY, maxY)]).range([0, innerHeight]).nice();
2.2.2 line()
const line = d3
.line()
.x((d: IData) => {
return xScale(xValue(d)) || 0;
})
.y((d: IData) => {
return yScale(yValue(d)) || 0;
})
.curve(d3.curveCardinal.tension(0.5));
const pathEnter = pathUpdate
.enter()
.append("path")
.attr("class", "datacurve")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2.5)
.attr("d", line(data) || "");
报错
- x(d: IData)处:
Type ‘[number, number]‘ is not assignable to type ‘IData‘- attr("d", line(data) || ""):
Argument of type ‘IData[]‘ is not assignable to parameter of type ‘[number, number][]‘
都是说类型不匹配
const line = d3
.line<IData>()
.x((d: IData) => {
return xScale(xValue(d)) || 0;
})
.y((d: IData) => {
return yScale(yValue(d)) || 0;
})
.curve(d3.curveCardinal.tension(0.5));
const pathEnter = pathUpdate
.enter()
.append("path")
.attr("class", "datacurve")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2.5)
.attr("d", line(data) || "");
d3.line()用于生成一个line生成器,由于生成器不知道它将来会调用什么样的datum来生成line,所以使用泛型在编译阶段来限制类型。
2.2.3 selection.merge()
const pathUpdate = maingroup.selectAll(".datacurve").data(data);
const pathEnter = pathUpdate
.enter()
.append("path")
.attr("class", "datacurve")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2.5)
.attr("d", line(data) || "");
pathUpdate
.merge(pathEnter)
.transition()
.duration(2000)
.ease(d3.easeLinear)
.attr("d", lineEmpty(data) || "");
报错
pathEnter和pathUpdate泛型不相同,无法合并
pathEnter: d3.Selection<SVGPathElement, IData, d3.BaseType, unknown>
pathUpdate: d3.Selection<d3.BaseType, IData, d3.BaseType, unknown>
pathUpdate
.merge(d3.selectAll(".datacurve"))
.transition()
.duration(2000)
.ease(d3.easeLinear)
.attr("d", lineEmpty(data) || "");
这里我们使用selectAll来获得泛型相同的DOM
结束
拥抱TS是一个漫长且疼苦的过程。使用TS来操作第三方库时,单单熟悉API是不够的,还要了解设计的思想(声明的泛型和类型)避免陷入类型陷阱。
整个DBUG的过程足足有一下午,尝试过查看官方文档、接口声明,最终将问题定位到泛型上。
原文地址:https://www.cnblogs.com/xiaoxu-xmy/p/13762730.html
作者: lemon
原文:https://www.cnblogs.com/xiaoxu-xmy/p/13762730.html
内容总结
以上是互联网集市为您收集整理的原创 - react + TS + d3.js 实现曲线图 报错问题解决全部内容,希望文章能够帮你解决原创 - react + TS + d3.js 实现曲线图 报错问题解决所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。