reactjs - 如何从反应组件道具中进行 ChartJS 配置的类型传输?
问题描述
我制作了一个ChartJS
组件,它是一个简单的反应包装器,可以使用一些默认选项正确设置和销毁 ChartJS 实例:
import React, {
useEffect,
useState,
VFC,
} from 'react';
import {
ArcElement,
BarController,
BarElement,
CategoryScale,
Chart,
ChartConfiguration,
DoughnutController,
LinearScale,
} from 'chart.js';
Chart.register(
ArcElement,
BarElement,
BarController,
DoughnutController,
CategoryScale,
LinearScale,
);
export interface ChartJSProps {
config: ChartConfiguration;
}
export const ChartJS: VFC<ChartJSProps> = ({
config,
}) => {
const [id, setId] = useState<string>();
useEffect(() => {
setId(`chart-${Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1)}`);
}, []);
useEffect(() => {
if (!id) {
return;
}
// @see https://stackoverflow.com/a/58877962/1731473
const canvas: HTMLCanvasElement = document.getElementById(id) as HTMLCanvasElement;
const ctx = canvas.getContext('2d');
let chart: Chart;
if (ctx !== null) {
const { options } = config;
chart = new Chart(ctx, {
...config,
options: {
responsive: true,
maintainAspectRatio: false,
...options,
elements: {
...options?.elements,
line: {
fill: false,
...options?.elements?.line,
},
},
plugins: {
...options?.plugins,
legend: {
display: false,
...options?.plugins?.legend,
},
tooltip: {
mode: 'nearest',
intersect: false,
...options?.plugins?.tooltip,
},
},
},
});
}
return () => {
if (chart) {
// @see https://www.chartjs.org/docs/latest/developers/api.html#destroy
chart.destroy();
}
};
}, [id]);
return (
<canvas id={id} />
);
};
export default null;
这个包装器可以正常工作,但是,Typescript 对特定类型的配置大喊大叫。
下面以一个GaugeChart
使用 ChartJSdoughnut
类型的组件为例:
import React, {
VFC,
} from 'react';
import { ChartJS } from './ChartJS';
export interface GaugeChartProps {
value: number;
max?: number;
}
export const GaugeChart: VFC<GaugeChartProps> = ({
value,
max = 100,
}) => {
const percent = value / max;
return (
<ChartJS config={{
type: 'doughnut',
data: {
datasets: [
{
backgroundColor: [
'rgb(255, 99, 132)',
'#ccc',
],
// eslint-disable-next-line id-blacklist
data: [
percent * 100,
100 - (percent * 100),
],
},
],
},
options: {
rotation: 270, // start angle in degrees
circumference: 180, // sweep angle in degrees
},
}}
/>
);
};
export default null;
该npx tsc
命令给了我这个:
src/components/chart/GaugeChart.tsx:36:9 - error TS2322: Type '{ rotation: number; circumference: number; }' is not assignable to type '_DeepPartialObject<CoreChartOptions<keyof ChartTypeRegistry> & ElementChartOptions<keyof ChartTypeRegistry> & PluginChartOptions<...> & DatasetChartOptions<...> & ScaleChartOptions<...>>'.
Object literal may only specify known properties, and 'rotation' does not exist in type '_DeepPartialObject<CoreChartOptions<keyof ChartTypeRegistry> & ElementChartOptions<keyof ChartTypeRegistry> & PluginChartOptions<...> & DatasetChartOptions<...> & ScaleChartOptions<...>>'.
36 rotation: 270, // start angle in degrees
~~~~~~~~~~~~~
node_modules/chart.js/types/index.esm.d.ts:3493:3
3493 options?: ChartOptions<TType>;
~~~~~~~
The expected type comes from property 'options' which is declared here on type 'ChartConfiguration<keyof ChartTypeRegistry, (number | ScatterDataPoint | BubbleDataPoint | null)[], unknown>'
打字稿版本:
❯ npx tsc -v
Version 4.2.2
如您所见,该rotation
选项无法识别。
当然,我可以通过声明一个类型良好的常量来解决它:
import React, {
VFC,
} from 'react';
import { ChartConfiguration } from 'chart.js';
import { ChartJS } from './ChartJS';
export const GaugeChart: VFC<GaugeChartProps> = ({
value,
max = 100,
}) => {
const percent = value / max;
const config: ChartConfiguration<'doughnut'> = {
type: 'doughnut',
data: {
datasets: [
{
backgroundColor: [
'rgb(255, 99, 132)',
'#ccc',
],
// eslint-disable-next-line id-blacklist
data: [
percent * 100,
100 - (percent * 100),
],
},
],
},
options: {
rotation: 270, // start angle in degrees
circumference: 180, // sweep angle in degrees
},
};
return <ChartJS config={config} />;
};
但这意味着我必须直接依赖chart.js
供应商,这看起来并不理想。
此外,type
配置值应该推断定义,因为它是通过直接配置options
的简单调用完成的。new Chart
如何type
在我的组件包装器config
道具上具有与值推断相同的行为?
解决方案
推荐阅读
- core-data - 多个 NSEntityDescriptions 声明 NSManagedObject 子类
- javascript - 如何在whatsapp的javascript中增加按钮大小
- mysql - 使用表 1 中而不是表 2 中的外键进行更高效的查询
- javascript - 通过使用选择器而不是 ID 定位来简化对元素的类操作
- ssh - 为遗留应用程序的打开套接字连接创建 SSH 隧道
- android - Android Kotlin findViewById 不能为空
- java - Ebean中的条件@OnetoMany关系
- c# - 我不断收到异常功能“插值字符串”不能使用,因为它不是 C# 4.0 语言的一部分?
- assembly - 提供一个使用 Rust 的内联汇编查询 Intel CPU 能力的例子
- c# - 如何从(从串行端口读取的响应)文本框中显示的行数中删除,替换或子串字符串