我的 React 组件中 useCallback 挂钩的意外行为
Unexpected behavior of useCallback hook in my React component
我是 React 的新手,Javascript space。我正在尝试构建一个使用 HighCharts 库的组件,我正在实现的其中一件事是格式化程序回调。此回调需要访问从父组件传递给此组件的道具。我正在使用的 属性 称为 metricUnit
。我现在注意到的行为是,即使组件中的 metricUnit
更新,它也不会传递到此回调函数。我在这里做错了什么?
const BoxChart = ({ panelId, group, metric, metricUnit, min, max,
data}) => {
console.log(panelId, metric, metricUnit) // this shows the updated value
const pointFormatterCallback = useCallback(function() {
return function() {
console.log(metric);
var value = this.y;
if (value % 1) {
value = Highcharts.numberFormat(value, 2);
} else {
value = Highcharts.numberFormat(value, 0);
}
const unitStr = metricUnit ? " (" + metricUnit + ")" : ""; //this still shows the older value
return this.name + ': ' + value + unitStr + '<br/>';
}
}, [metric, metricUnit]);
const [defaultChartOptions, setDefaultChartOptions] = useState({
chart: {
style: {
fontFamily: '"Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;',
width: '100%'
},
type: 'boxplot',
zoomType: 'x'
},
xAxis: {
categories: [],
title: {
text: group,
style: {
fontWeight: 'bold'
}
}
},
yAxis: {
title: {
text: metric,
style: {
fontWeight: 'bold'
}
},
labels: {
formatter: function() {
var outputVal = this.value;
if(outputVal % 1) {
outputVal = Highcharts.numberFormat(this.value, 2);
} else {
outputVal = Highcharts.numberFormat(this.value, 0);
}
return outputVal;
}
},
startOnTick: false,
endOnTick: false
},
title: {
text: null
},
credits: {
enabled: false
},
legend: {
maxHeight:100,
margin: 6
},
plotOptions: {
boxplot: {
tooltip: {
valueDecimals: 2
}
},
scatter: {
marker: {
radius: 5
},
tooltip: {
headerFormat: "<b>{series.name}</b><br>",
pointFormatter: pointFormatterCallback(),
valueDecimals: 2
}
}
}
});
const chartWrapper = useRef(null);
const { width } = useElementSize(chartWrapper)
const chartComponent = useRef(null);
useEffect(() => {
console.log("Current width", width);
if(chartComponent.current) {
const chart = chartComponent.current.chart;
chart.update({
chart: {
width: width
}
});
}
}, [width]);
if(data === undefined || Object.keys(data).length === 0 || data.categories.length === 0) {
return(<div style={{position: 'absolute', top: '50%', left: '50%', transform: 'translateX(-50%) translateY(-50%)'}}>No data available</div>);
} else {
const series = data['series'];
const categories = data['categories'];
const chartOptions = {...defaultChartOptions};
const unitStr = metricUnit ? " (" + metricUnit + ")" : "";
chartOptions['yAxis']['title']['text'] = metric + unitStr;
chartOptions['xAxis']['title']['text'] = group;
chartOptions['series'] = series;
chartOptions['xAxis']['categories'] = categories;
if(min !== null && !isNaN(min)) {
chartOptions['yAxis']['min'] = min;
}
if(max !== null && !isNaN(max)) {
chartOptions['yAxis']['max'] = max;
}
return (
<div ref={chartWrapper} style={{overflow: "hidden", width: "100%"}}>
<HighchartsReact highcharts={Highcharts} options={chartOptions}
ref={chartComponent}
/>
</div>
);
}
}
这是一个示例再现:
https://codesandbox.io/s/determined-tesla-26z00?file=/demo.jsx
现在您的回调传递给 useCallback
returns 一个函数来完成工作。因此会有一些意想不到的关闭行为。因此,我最好的解释是:
当您将 pointFormatterCallback
设置为 pointFormatter
时,您将调用它,返回函数中的 metricUnit 值将是它在 调用时设置的任何值。为了在更新时读取指标,您必须重新调用 pointFormatterCallback
才能访问新更新的指标值。
尝试像这样删除返回的函数:
const pointFormatterCallback = useCallback(function() {
console.log(metric);
var value = this.y;
if (value % 1) {
value = Highcharts.numberFormat(value, 2);
} else {
value = Highcharts.numberFormat(value, 0);
}
const unitStr = metricUnit ? " (" + metricUnit + ")" : ""; //this still shows the older value
return this.name + ': ' + value + unitStr + '<br/>';
}, [metric, metricUnit]);
请注意,更改父状态不会重新呈现 tooltipCallback
函数,它仍然引用前一个单元。
演示:https://codesandbox.io/s/patient-fog-mxdjr?file=/demo.jsx:708-723 - 检查控制台以了解我在说什么。
我认为如果已经更改,您应该使用 useEffect
挂钩来设置此参数。
您甚至可以通过更新工具提示格式化程序来简化它 - 它让您确定图表组件将被重新呈现。
我是 React 的新手,Javascript space。我正在尝试构建一个使用 HighCharts 库的组件,我正在实现的其中一件事是格式化程序回调。此回调需要访问从父组件传递给此组件的道具。我正在使用的 属性 称为 metricUnit
。我现在注意到的行为是,即使组件中的 metricUnit
更新,它也不会传递到此回调函数。我在这里做错了什么?
const BoxChart = ({ panelId, group, metric, metricUnit, min, max,
data}) => {
console.log(panelId, metric, metricUnit) // this shows the updated value
const pointFormatterCallback = useCallback(function() {
return function() {
console.log(metric);
var value = this.y;
if (value % 1) {
value = Highcharts.numberFormat(value, 2);
} else {
value = Highcharts.numberFormat(value, 0);
}
const unitStr = metricUnit ? " (" + metricUnit + ")" : ""; //this still shows the older value
return this.name + ': ' + value + unitStr + '<br/>';
}
}, [metric, metricUnit]);
const [defaultChartOptions, setDefaultChartOptions] = useState({
chart: {
style: {
fontFamily: '"Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;',
width: '100%'
},
type: 'boxplot',
zoomType: 'x'
},
xAxis: {
categories: [],
title: {
text: group,
style: {
fontWeight: 'bold'
}
}
},
yAxis: {
title: {
text: metric,
style: {
fontWeight: 'bold'
}
},
labels: {
formatter: function() {
var outputVal = this.value;
if(outputVal % 1) {
outputVal = Highcharts.numberFormat(this.value, 2);
} else {
outputVal = Highcharts.numberFormat(this.value, 0);
}
return outputVal;
}
},
startOnTick: false,
endOnTick: false
},
title: {
text: null
},
credits: {
enabled: false
},
legend: {
maxHeight:100,
margin: 6
},
plotOptions: {
boxplot: {
tooltip: {
valueDecimals: 2
}
},
scatter: {
marker: {
radius: 5
},
tooltip: {
headerFormat: "<b>{series.name}</b><br>",
pointFormatter: pointFormatterCallback(),
valueDecimals: 2
}
}
}
});
const chartWrapper = useRef(null);
const { width } = useElementSize(chartWrapper)
const chartComponent = useRef(null);
useEffect(() => {
console.log("Current width", width);
if(chartComponent.current) {
const chart = chartComponent.current.chart;
chart.update({
chart: {
width: width
}
});
}
}, [width]);
if(data === undefined || Object.keys(data).length === 0 || data.categories.length === 0) {
return(<div style={{position: 'absolute', top: '50%', left: '50%', transform: 'translateX(-50%) translateY(-50%)'}}>No data available</div>);
} else {
const series = data['series'];
const categories = data['categories'];
const chartOptions = {...defaultChartOptions};
const unitStr = metricUnit ? " (" + metricUnit + ")" : "";
chartOptions['yAxis']['title']['text'] = metric + unitStr;
chartOptions['xAxis']['title']['text'] = group;
chartOptions['series'] = series;
chartOptions['xAxis']['categories'] = categories;
if(min !== null && !isNaN(min)) {
chartOptions['yAxis']['min'] = min;
}
if(max !== null && !isNaN(max)) {
chartOptions['yAxis']['max'] = max;
}
return (
<div ref={chartWrapper} style={{overflow: "hidden", width: "100%"}}>
<HighchartsReact highcharts={Highcharts} options={chartOptions}
ref={chartComponent}
/>
</div>
);
}
}
这是一个示例再现: https://codesandbox.io/s/determined-tesla-26z00?file=/demo.jsx
现在您的回调传递给 useCallback
returns 一个函数来完成工作。因此会有一些意想不到的关闭行为。因此,我最好的解释是:
当您将 pointFormatterCallback
设置为 pointFormatter
时,您将调用它,返回函数中的 metricUnit 值将是它在 调用时设置的任何值。为了在更新时读取指标,您必须重新调用 pointFormatterCallback
才能访问新更新的指标值。
尝试像这样删除返回的函数:
const pointFormatterCallback = useCallback(function() {
console.log(metric);
var value = this.y;
if (value % 1) {
value = Highcharts.numberFormat(value, 2);
} else {
value = Highcharts.numberFormat(value, 0);
}
const unitStr = metricUnit ? " (" + metricUnit + ")" : ""; //this still shows the older value
return this.name + ': ' + value + unitStr + '<br/>';
}, [metric, metricUnit]);
请注意,更改父状态不会重新呈现 tooltipCallback
函数,它仍然引用前一个单元。
演示:https://codesandbox.io/s/patient-fog-mxdjr?file=/demo.jsx:708-723 - 检查控制台以了解我在说什么。
我认为如果已经更改,您应该使用 useEffect
挂钩来设置此参数。
您甚至可以通过更新工具提示格式化程序来简化它 - 它让您确定图表组件将被重新呈现。