2016-04-14 12 views
2

反応コンポーネントの内部にサードパーティのライブラリを使用するのが面倒なバグがあります。私はこのポストのために考案されたデモを再現することができました。React.jsサードパーティのライブラリ実装を扱う

チャートライブラリであるc3jsを使って、componentDidMount()にレンダリングし、への正しい呼び出しでcomponentWillUnmount()でそれを取り除くことから説明しましょう。

コンポーネント自体をフィルタリングするときにバグが発生します。基本的にはコンポーネントが正しくフィルタリングされますが、コンポーネント内にある実際のチャートは最初のチャートと同じままです。基本的に間違ったコンポーネントの内部にある間違ったチャートです!

Remove all charts except chart 3ボタンをクリックすると、図表にchartidというラベルが付けられ、フィルタリングによって他の図が正しく削除されます。

フィルタリングが正しく機能し、ビューが更新されるため、私のコードではありません。グラフにラベルを付けて、ビューに表示できるので、確認できます。コンソールエラーはなく、自分のコードが動作していることを確認しました。

私の質問は - c3jsを使ってこの制限を回避することができますか、これは実際に私のコードとチャートをレンダリングする方法の問題です。

関連デモ:https://jsfiddle.net/69z2wepo/38614/

関連コード:

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') 
); 

答えて

1

問題は、あなたのチャート上のキーを設定している方法です。レンダラーは、どのチャートを保持しようとしているのか混乱させてしまいます。どのようにReact handles keysを見てみましょう代わりに<Chart key={i} data={metrics} />

<Chart key={data[i].chartid} data={metrics} />

はこれを試してみてください。 は、コンポーネントのライフサイクルのキーを持つ子であるを一意に識別していることに注意してください。したがって、チャート1はキー「1」によって一意に識別されるため、チャート3をキー「1」でレンダリングすることはできません。上記の私の解決策は、チャートがそのレンダリング順序ではなく、チャートIDによって一意に識別されるようにします。

+0

はい、あなたは正しいです!私が割り当てていたキーは実際に 'map'ループに基づいていて実際にチャートには関係していなかったので、チャート3はフィルタリングされているときに' key = {1} 'を受け取りました。私はそれを述べて正しいですか? – AntonB

+1

これはまさに正しいことです。振る舞いはちょっと直感的ではありませんが、Reactには高いパフォーマンスが得られるような最適化があることを覚えておいてください。 – TAGraves