问题描述
通常,effect
中的React.useEffect
的{{1}}仅在deps
列表中的值更改时被激活。
我想要的是相反的。我希望它被激活,除非列表更改中的值。
。顺便说一句,我知道chart.js周围的包装器已经存在。
例如:
import React,{ useState,useEffect,memo,useMemo } from "react";
import merge from "lodash/merge";
import {
Chart as ChartJS,ChartType,ChartData,ChartOptions,ChartConfiguration,} from "chart.js";
type ChartInstance = InstanceType<typeof ChartJS> | null;
export type ChartProps = Readonly<ChartData> &
Readonly<ChartOptions> & {
readonly type?: ChartType;
readonly height?: number | string;
};
export const Chart = memo<ChartProps>(
({
type = "line",height = "20em",responsive = true,maintainAspectRatio = false,animation = { duration: 0 },hover = { animationDuration: 0 },responsiveAnimationDuration = 0,labels = [],datasets = [],legend: { display = false,...legend } = {},...rest
}) => {
const [canvas,ref] = useState<HTMLCanvasElement | null>(null);
let chart: ChartInstance = null;
const options: ChartConfiguration = {
type,data: { labels,datasets },options: {
responsive,maintainAspectRatio,animation,hover,responsiveAnimationDuration,legend: { display,...legend },...rest,},};
const destroy = () => {
if (chart) {
chart.destroy();
chart = null;
}
};
chart = useMemo<ChartInstance>(() => {
destroy();
return canvas ? new ChartJS(canvas,options) : null;
},[canvas,type]);
useEffect(() => {
if (chart) {
merge(chart,options).update();
}
});
useEffect(() => destroy,[]);
return (
<div style={{ position: "relative",height }}>
<canvas ref={ref} />
</div>
);
}
);
如果我们只是创建图表,则更新图表是没有意义的。
如果执行useMemo
,则后面的useEffect
不应该执行。
否则,它需要每次执行。
该组件包装在React.memo
中,因此,如果发生渲染,
这意味着某些图表道具已更改,因此需要重新创建或更新图表。
解决方法
没有一种简单的方法来还原useEffect
依赖性检查行为。可以做到,但这将是不必要的hack,尤其是您不使用清理功能。
在您的情况下,最直接的方法是引入一个变量(shouldUpdateChart
),该变量将控制是否应运行useEffect
代码。如果运行useMemo
中的代码,则其值为false
,否则为true
:
let shouldUpdateChart = true;
chart = useMemo < ChartInstance > (() => {
shouldUpdateChart = false;
destroy();
return canvas ? new ChartJS(canvas,options) : null;
},[canvas,type]);
useEffect(() => {
if (shouldUpdateChart) {
merge(chart,options).update();
}
});
,
一种选择是维持对变量先前值的一些外部引用,在每个渲染器上运行效果,然后在变量发生更改时纾困。这是一个可能如何工作的示例。您可以将此逻辑抽象为自己的自定义钩子。
let prevA;
export default function App() {
const [a,setA] = useState(0);
const [b,setB] = useState(0);
useEffect(() => {
if (a !== prevA) {
prevA = a;
return;
}
console.log("a did not change");
});
return (
<div className="App">
<button
onClick={() => {
setA(a + 1);
}}
>
Increment A
</button>
<button
onClick={() => {
setB(b + 1);
}}
>
Increment B
</button>
</div>
);
}