我正在使用react-apexcharts来可视化一些财务数据.我的图表有一个在百分比和美元之间切换y轴刻度的选项.虽然当我单击相应的按钮时,比例会正确更改,但轴标签(符号%和$)不会动态更新.只有当我快速刷新时,它们才会改变.以下是一个演示:

const { createRoot } = ReactDOM;
const { Fragment, StrictMode, useEffect, useState } = React;
const Chart = ReactApexChart;

const App = () => {
  const baseChartOptions = {
    chart: {
      type: 'line',
    },
    series: [
      {
        name: 'serie1',
        data: [1, 2, 3],
      },
    ],
    xaxis: {
      categories: ['a', 'b', 'c'],
    },
  };
  const [chartOptions, setChartOptions] = useState(baseChartOptions);
  const [scale, setScale] = useState('percentage');

  useEffect(() => {
    const getYAxisFormatter = (val) => {
      return scale === 'percentage' ? `${val.toFixed(1)}%` : `$${val.toFixed(2)}`;
    };

    setChartOptions((prevOptions) => ({
      ...prevOptions,
      yaxis: { labels: { formatter: getYAxisFormatter } },
    }));
  }, [scale]);

  return (
    <Fragment>
      <h4>{scale}</h4>
      <button onClick={() => setScale('dollars')}>Dollars</button>
      <button onClick={() => setScale('percentage')}>Percentage</button>
      <Chart options={chartOptions} series={chartOptions.series} type="bar" width="500" height="320" />
    </Fragment>
  );
};

const root = createRoot(document.getElementById("root"));
root.render(<StrictMode><App /></StrictMode>);
body {
  font-family: sans-serif;
}
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script crossorigin src="https://unpkg.com/prop-types@15.8.1/prop-types.js"></script>
<script crossorigin src="https://unpkg.com/apexcharts@3.42.0/dist/apexcharts.js"></script>
<script crossorigin src="https://unpkg.com/react-apexcharts@1.4.1/dist/react-apexcharts.iife.min.js"></script>

推荐答案

Problem

问题始于react-apexcharts对期权变化的react .以下是组件props 更新时运行的代码片段:

componentDidUpdate (prevProps) {
  if (!this.chart) return null
  const { options, series, height, width } = this.props
  const prevOptions = JSON.stringify(prevProps.options)
  const prevSeries = JSON.stringify(prevProps.series)
  const currentOptions = JSON.stringify(options)
  const currentSeries = JSON.stringify(series)

  if (
    prevOptions !== currentOptions ||
    prevSeries !== currentSeries ||
    height !== prevProps.height ||
    width !== prevProps.width
  ) {
    if (prevSeries === currentSeries) {
      // series has not changed, but options or size have changed
      this.chart.updateOptions(this.getConfig())
    } else if (
      prevOptions === currentOptions &&
      height === prevProps.height &&
      width === prevProps.width
    ) {
      // options or size have not changed, just the series has changed
      this.chart.updateSeries(series)
    } else {
      // both might be changed
      this.chart.updateOptions(this.getConfig())
    }
  }
}

-reference-reference

从上面的代码片段可以看出,它将以前的和当前的选项转换为JSON字符串进行比较.当比例发生变化时,会引用一个新的格式化程序函数.然而,在JSON.stringify中,函数被转换为空对象,因此结果JSON字符串在进行比较时是相同的.以下是一个演示,说明了这一点:

function formatter1() {}

function formatter2() {}

const prevOptions = {
  chart: {
    type: 'line',
  },
  series: [
    {
      name: 'serie1',
      data: [1, 2, 3],
    },
  ],
  xaxis: {
    categories: ['a', 'b', 'c'],
  },
  yaxis: {
    labels: {
      formatter: formatter1,
    }
  }
};

const currentOptions = {
  chart: {
    type: 'line',
  },
  series: [
    {
      name: 'serie1',
      data: [1, 2, 3],
    },
  ],
  xaxis: {
    categories: ['a', 'b', 'c'],
  },
  yaxis: {
    labels: {
      formatter: formatter2,
    }
  }
};

// Logs `true` even though the options have different formatters
console.log(JSON.stringify(prevOptions) === JSON.stringify(currentOptions));

Solution

An ok solution

通过了解react-apexcharts个不同选项的不同之处,您可以向对象添加一个在比例更改时更改的关键点,以提示差异算法需要进行更新.在下面的演示中,scale被添加到options对象中(参见对setChartOptions的调用):

const { createRoot } = ReactDOM;
const { Fragment, StrictMode, useEffect, useState } = React;
const Chart = ReactApexChart;

const App = () => {
  const baseChartOptions = {
    chart: {
      type: 'line',
    },
    series: [
      {
        name: 'serie1',
        data: [1, 2, 3],
      },
    ],
    xaxis: {
      categories: ['a', 'b', 'c'],
    },
  };
  const [chartOptions, setChartOptions] = useState(baseChartOptions);
  const [scale, setScale] = useState('percentage');

  useEffect(() => {
    const getYAxisFormatter = (val) => {
      return scale === 'percentage' ? `${val.toFixed(1)}%` : `$${val.toFixed(2)}`;
    };

    setChartOptions((prevOptions) => ({
      ...prevOptions,
      yaxis: { labels: { formatter: getYAxisFormatter } },
      scale,
    }));
  }, [scale]);

  return (
    <Fragment>
      <h4>{scale}</h4>
      <button onClick={() => setScale('dollars')}>Dollars</button>
      <button onClick={() => setScale('percentage')}>Percentage</button>
      <Chart options={chartOptions} series={chartOptions.series} type="bar" width="500" height="320" />
    </Fragment>
  );
};

const root = createRoot(document.getElementById("root"));
root.render(<StrictMode><App /></StrictMode>);
body {
  font-family: sans-serif;
}
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script crossorigin src="https://unpkg.com/prop-types@15.8.1/prop-types.js"></script>
<script crossorigin src="https://unpkg.com/apexcharts@3.42.0/dist/apexcharts.js"></script>
<script crossorigin src="https://unpkg.com/react-apexcharts@1.4.1/dist/react-apexcharts.iife.min.js"></script>

A better solution

为什么chartOptions需要存储在州中?您可能只需要将其作为一个普通的旧式JavaScript对象.这将通过消除设置和更新图表选项状态的代码来简化您的代码.从React's documentation人起:

避免冗余状态

如果您可以在呈现期间从组件的props 或其现有状态变量计算出一些信息,则可以将这些信息放入该组件的状态中.

如果你担心表现,那么getYAxisFormatter附近的useCallbackchartOptions附近的useMemo可能是有用的.但是,与所有性能优化一样,建议首先进行基准测试.

如上所述,仍然需要向差异算法提供组件需要重新呈现的提示.使用删除冗余状态的重构,这可以通过两种方式完成:

  1. scale作为键添加到chartOptions对象(与ok解决方案类似).
  2. scale分到Chart分的key prop分.当REACT与DOM不同时,key中的更改提示REACT组件需要重新呈现.这个选项可能比上面的选项更好,因为它是一个显式的API--如果react-apexcharts更改了它的实现细节,前面的选项可能不起作用.以下是一个演示:

const { createRoot } = ReactDOM;
const { Fragment, StrictMode, useEffect, useState } = React;
const Chart = ReactApexChart;

const App = () => {
  const [scale, setScale] = useState('percentage');
  
  const getYAxisFormatter = (val) => {
    return scale === 'percentage' ? `${val.toFixed(1)}%` : `$${val.toFixed(2)}`;
  };

  const chartOptions = {
    chart: {
      type: 'line',
    },
    series: [
      {
        name: 'serie1',
        data: [1, 2, 3],
      },
    ],
    xaxis: {
      categories: ['a', 'b', 'c'],
    },
    yaxis: {
      labels: {
        formatter: getYAxisFormatter
      }
    }
  };

  return (
    <Fragment>
      <h4>{scale}</h4>
      <button onClick={() => setScale('dollars')}>Dollars</button>
      <button onClick={() => setScale('percentage')}>Percentage</button>
      <Chart key={scale} options={chartOptions} series={chartOptions.series} type="bar" width="500" height="320" />
    </Fragment>
  );
};

const root = createRoot(document.getElementById("root"));
root.render(<StrictMode><App /></StrictMode>);
body {
  font-family: sans-serif;
}
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script crossorigin src="https://unpkg.com/prop-types@15.8.1/prop-types.js"></script>
<script crossorigin src="https://unpkg.com/apexcharts@3.42.0/dist/apexcharts.js"></script>
<script crossorigin src="https://unpkg.com/react-apexcharts@1.4.1/dist/react-apexcharts.iife.min.js"></script>

Reactjs相关问答推荐

我想将状态设置为true,直到每一张卡片都生成并在多姆中呈现

导致类型错误的调度不是函数";

cypress 不能点击SPAN

ReactJs;Reaction-Hook-Form:仅当 Select 了特定 Select 列表选项时才显示文本输入

使用下一步中的路由/导航不会立即加载路由

在Map()完成React.js中的多个垃圾API请求后执行函数

如何清除过滤器搜索的状态

Javascript - 正则表达式不适用于 MAC OS - 编码?

如何管理组件列表的复杂状态? (Nextjs/ReactJS)

如何解决使用react-hook-form和yup动态创建的输入不集中的问题

使用reactrouterdom时显示"对象无效作为React子组件"错误

当`useSelector`回调中的属性值更新时,将会使用更新后的值吗?

next js 13 在获取中使用标头时动态渲染而不是静态渲染?

RTK 查询Mutations 在 StrictMode 中执行两次

动态添加或删除文本输入字段并将值设置为 React JS 中主要状态的数组

如何在props 更改时运行自定义挂钩?

react-markdown 不渲染 Markdown

如何在组件文件夹内的react 组件文件中导入外部css文件?

我想将悬停 colored颜色 更改为红色.写了一个话题,这个元素没有react ,怎么解决呢?

我可以在类组件中使用 useState 挂钩吗?