我正在尝试使用d3.js创建折线图,并将其实现为带有钩子的功能组件。
我尝试使用useRef
。将它们初始化为null,然后在JSX中进行设置,如代码中所示。当我要使用使用clientWidth
ref的lineChart
时,它表示为空。
我想到创建一个initializeDrawing函数,该函数将初始化空的赋值,但是由于组件不会在变量更改时刷新,因此它不起作用。我还考虑过将这些变量携带到useState
中,但这似乎不是一个好的解决方案。
我想向您学习什么是用功能组件实现d3.js图表的最佳解决方案。
我正在尝试实现this example,但是学习实现d3.js以使函数组件具有钩子的一般原则是主要目标。
代码如下:
...
...
...
const LineChart: FunctionComponent = (props) => {
let dataArrIndex = 0;
const lineChart = useRef((null as unknown) as HTMLDivElement);
const xAxis = useRef((null as unknown) as SVGGElement);
const yAxis = useRef((null as unknown) as SVGGElement);
const margin = { top: 40, right: 40, bottom: 40, left: 40 };
const aspectRatio = 9 / 16;
const chartWidth = lineChart.current.clientWidth - margin.left - margin.right;
const chartHeight =
lineChart.current.clientWidth * aspectRatio - margin.top - margin.bottom;
const yScale = d3.scaleLinear().range([chartHeight, 0]);
const xScale = d3.scaleBand().range([0, chartWidth]);
const update = () => {
yScale.domain([
0,
d3.max(
data,
(d): number => {
return d[dataArrIndex].balance;
},
) as number,
]);
xScale.domain(data[dataArrIndex].map((d) => d.month));
d3.select(yAxis.current).call(d3.axisLeft(yScale));
d3.select(xAxis.current).call(d3.axisBottom(xScale));
};
return (
<div className="line-chart" ref={lineChart}>
<svg width={chartWidth} height={chartHeight}>
<g transform={`translate(${margin.left},${margin.bottom})`} />
<g ref={yAxis} />
<g ref={xAxis} />
<path />
</svg>
</div>
);
};
export default LineChart;
答案 0 :(得分:0)
问题是您尝试在dom中创建此元素之前对元素使用ref,因此您具有null而不是对htmlelement的引用。要解决您的问题,您需要包装所有使用refs来使用useEffect挂钩的代码。
类似这样的东西:
useEffect(() => {
const chartWidth = lineChart.current.clientWidth - margin.left - margin.right;
const chartHeight =
lineChart.current.clientWidth * aspectRatio - margin.top - margin.bottom;
const yScale = d3.scaleLinear().range([chartHeight, 0]);
const xScale = d3.scaleBand().range([0, chartWidth]);
const update = () => {
yScale.domain([
0,
d3.max(
data,
(d): number => {
return d[dataArrIndex].balance;
},
) as number,
]);
xScale.domain(data[dataArrIndex].map((d) => d.month));
d3.select(yAxis.current).call(d3.axisLeft(yScale));
d3.select(xAxis.current).call(d3.axisBottom(xScale));
};
}
使用效果在功能组件中用作componentDidMount / componentDidUpdate / componentWillUnmount的类似物
如果每次组件接收更新时都需要调用方法“ update”,则需要将其从useEffect移到函数体并在ueEffect中调用:
const update = () => {
yScale.domain([
0,
d3.max(
data,
(d): number => {
return d[dataArrIndex].balance;
},
) as number,
]);
xScale.domain(data[dataArrIndex].map((d) => d.month));
d3.select(yAxis.current).call(d3.axisLeft(yScale));
d3.select(xAxis.current).call(d3.axisBottom(xScale));
};
useEffect(() => {
const chartWidth = lineChart.current.clientWidth - margin.left - margin.right;
const chartHeight =
lineChart.current.clientWidth * aspectRatio - margin.top - margin.bottom;
const yScale = d3.scaleLinear().range([chartHeight, 0]);
const xScale = d3.scaleBand().range([0, chartWidth]);
update();
}
您可以通过多种方式使用宽度高度之类的属性:
使用useState挂钩将其设置为状态:const {width,setWidth} = useState(0)并从ref的useEffect中对其进行更新
检查ref!==空<svg width={chartRef && chartRef.current.offsetWidth} />