简体   繁体   English

React.js处理第三方库安装

[英]React.js handling 3rd party library mounting

I'm seeing an awkward bug using a third party library inside of a react component. 我看到一个在反应组件中使用第三方库的尴尬错误。 I was able to reproduce a contrived demo for this post. 我能够为这篇文章重现一个人为的演示。

Let me start by explaining that I am using c3js - which is a charting library and rendering it in componentDidMount() and removing it in componentWillUnmount() with the correct calls to this.chart.destroy() . 首先让我解释一下,我正在使用c3js - 这是一个图表库并在componentDidMount()呈现它,并使用对this.chart.destroy()的正确调用将其移除到componentWillUnmount() this.chart.destroy()

The bug occurs when filtering the components themselves, essentially what happens is the components are filtered correctly but the actual chart that sits inside the component stays the same as the first chart, which is very strange behaviour. 当过滤组件本身时会发生错误,基本上会发生的事情是组件被正确过滤但是组件内部的实际图表与第一个图表保持相同,这是非常奇怪的行为。 Basically it's the wrong chart inside the wrong component! 基本上它是错误组件内的错误图表!

You can see what I mean by clicking on the Remove all charts except chart 3 button, I have labeled the charts with a chartid and the filtering will correctly remove the other charts. 你可以看到我通过点击意味着Remove all charts except chart 3按钮,我已经标记图表与chartid和筛选将正确地删除其他图表。

I am fairly certain it isn't my code because the filtering works correctly and updates the view. 我相当肯定这不是我的代码,因为过滤工作正常并更新了视图。 You can verify because I have labeled the charts and it is visible in the view. 您可以验证,因为我已经标记了图表,并且它在视图中可见。 There is no console errors and I have verified my code works. 没有控制台错误,我已验证我的代码工作。

So my question is - can we work around this limitation using c3js, or is this really a problem with my code and the way I am rendering the charts. 所以我的问题是 - 我们可以使用c3js来解决这个限制,或者这对我的代码以及我渲染图表的方式来说确实是个问题。

Related demo: https://jsfiddle.net/69z2wepo/38614/ 相关演示: https//jsfiddle.net/69z2wepo/38614/

Related code: 相关代码:

var data = [
  {
    chartid: 1,
    columns: [
                ['x', 0, 1, 2, 3, 4, 5, 6],
                ['data1', 130, 300, 330, 400, 300, 400, 500],
                ['data2', 390, 230, 200, 150, 100, 130, 210],
                ['data3', 290, 430, 300, 160, 210, 170, 190],
                ['data4', 190, 330, 200, 260, 190, 250, 320]
    ]
  },
  {
    chartid: 2,
    columns: [
                ['x', 0, 1, 2, 3, 4, 5, 6],
                ['data1', 130, 300, 330, 400, 300, 400, 500],
                ['data2', 390, 230, 200, 150, 100, 130, 210],
                ['data3', 290, 430, 300, 160, 210, 170, 190]
    ]
  },
  {
    chartid: 3,
    columns: [
                ['x', 0, 1, 2, 3, 4, 5, 6],
                ['data1', 130, 300, 330, 400, 300, 400, 500],
                ['data2', 390, 230, 200, 150, 100, 130, 210]
    ]
  }
];

var Button = React.createClass({
  handleDelete: function (id) {
    this.props.handleDelete(id);
  },
  render: function() {
    return (
         <button onClick={this.handleDelete.bind(null, 3)}>
            Remove all charts except chart 3
         </button>
    )
  }
});

var Chart = React.createClass({
  componentDidMount() {
    this.chart = c3.generate({
        bindto: '.chart-' + this.props.data.chartid,
        data: {
            columns: this.props.data.columns
        }
    });
  },
  componentWillUnmount() {
    this.chart.destroy();
  },
  render: function() {
    return (
      <div>
        <h4>{"chart-" + this.props.data.chartid}</h4>
        <div className={"chart-" + this.props.data.chartid}>
        </div>
      </div>
      )
  }
});

var Child = React.createClass({
  renderCharts: function(data) {
    return data.map(function(metrics, i) {
      return (
        <Chart key={i} data={metrics} />
      )
    });
  },
  handleDelete: function(id) {
    this.props.handleDelete(id);
  },
  render: function() {
    return (
       <div>
         <Button handleDelete={this.handleDelete} />
         {this.renderCharts(this.props.data)}
       </div>
    ) 
  }
})

var App = React.createClass({
  getInitialState: function() {
    return {
      initialData: this.props.data
    }
  },
  handleDelete: function(id) {
     var _filterFunc = function(data) {
       if (data.chartid == id) return true;
       return false;
     };

     var _filterCharts = Array.prototype.filter.call(this.state.initialData, _filterFunc);

     this.setState({
        initialData: _filterCharts
     })
  },
  render: function() {
    return (
      <div>
        <Child handleDelete={this.handleDelete} data={this.state.initialData} />
      </div>
    );
  }
});

ReactDOM.render(
  <App data={data} />,
  document.getElementById('container')
);

The problem is the way you are setting the key on your chart. 问题是您在图表上设置密钥的方式。 It's causing the renderer to get confused about which chart you're trying to keep. 这导致渲染器对您要保留的图表感到困惑。

Try this: <Chart key={data[i].chartid} data={metrics} /> instead of <Chart key={i} data={metrics} /> 试试这个: <Chart key={data[i].chartid} data={metrics} />而不是<Chart key={i} data={metrics} />

Take a look at how React handles keys . 看看React如何处理密钥 Remember that you're uniquely identifying a child with a key for the lifecycle of the component. 请记住,您使用组件生命周期的键来唯一标识孩子。 So since chart 1 is uniquely identified by key "1", you can't render chart 3 with key "1." 因此,由于图表1由键“1”唯一标识,因此您无法使用键“1”呈现图表3。 My solution above ensures that the chart is uniquely identified by its chart id instead of by its rendering order. 我上面的解决方案确保图表由其图表ID唯一标识,而不是由其呈现顺序标识。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM