通常,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
中,因此,如果发生渲染,
这意味着某些图表道具已更改,因此需要重新创建或更新图表。
答案 0 :(得分:2)
没有一种简单的方法来还原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();
}
});
答案 1 :(得分:-1)
一种选择是维持对变量先前值的一些外部引用,在每个渲染器上运行效果,然后在变量发生更改时纾困。这是一个可能如何工作的示例。您可以将此逻辑抽象为自己的自定义钩子。
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>
);
}