【问题标题】:Unexpected behavior of useCallback hook in my React component我的 React 组件中 useCallback 挂钩的意外行为
【发布时间】:2021-04-01 20:06:36
【问题描述】:

我对 React 和 Javascript 领域还很陌生。我正在尝试构建一个使用 HighCharts 库的组件,我正在实现的一件事是格式化程序回调。这个回调需要访问从父组件传递给这个组件的 props。我正在使用的属性称为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

【问题讨论】:

  • 我看到您已经得到了答案,但如果您需要其他帮助,请在某些在线编辑器上重现您的问题。
  • @SebastianWędzel 我添加了复制链接。如果单击复选框,则工具提示中的指标不会更改

标签: javascript reactjs highcharts react-hooks


【解决方案1】:

请注意,更改父状态不会重新渲染 tooltipCallback 函数,它仍然具有对前一个单元的引用。

演示:https://codesandbox.io/s/patient-fog-mxdjr?file=/demo.jsx:708-723 - 检查控制台以了解我在说什么。

我认为如果已更改,您应该使用useEffect 挂钩来设置此参数。

您甚至可以通过更新工具提示格式化程序来简化它 - 它可以让您确保图表组件将被重新渲染。

演示:https://stackblitz.com/edit/react-ruwaxu?file=index.js

【讨论】:

    【解决方案2】:

    现在您传递给useCallback 的回调返回一个可以完成工作的函数。因此会有一些意想不到的关闭行为。因此,我最好的解释是:

    当您将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]);
    

    【讨论】:

    • 嗨贾斯汀,不幸的是这不起作用。我添加了一个复制到沙盒的链接
    猜你喜欢
    • 2021-07-01
    • 2020-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-18
    • 2021-11-11
    • 2022-08-19
    • 1970-01-01
    相关资源
    最近更新 更多