【2021年10月追記】Chart.js 3系ではredrawを使わなくても値の変更を反映することができるようになりました。Chart.js3.x系でグラフ上のクリック位置に値をセットする方法
react-chartjs-2を使ってグラフを表示する際、APIとの連携などを行うとグラフが複数回呼ばれることになります。chartjsは複数回renderを行うと不具合が生じるらしく、render前にdestroy()を呼び出す必要があるそうです。(https://www.chartjs.org/docs/latest/developers/api.html#destroy)
react-chartjs-2では下記のようにredrawを追加することで実現できました。
<LineChart data={data} redraw />
複数回renderで生じた問題
以前の記事でクリックする位置に値をセットする方法を説明しましたが、複数のdatasetを使用して複数回のrenderが行われる際にうまく値がセットされないという不具合が生じてしまいました。
function ChartInput(props){
const chartReference = createRef()
const data = () => {
return {
datasets: [
{
type: 'bar',
label: 'data0',
data: props.data[0],
backgroundColor: 'rgba(51, 255, 153, 0.2)',
borderColor: 'rgba(51, 255, 153, 1)',
borderWidth: 2,
},{
type: 'bar',
label: 'data1',
data: props.data[1],
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 2,
}
]
}
}
const optionsChartInput = () => {
return {
labels: props.labels,
scales: {
xAxes: [{
stacked: true,
labels: props.labels
}],
yAxes: [{
ticks: {
max: 100,
min: -100,
stepSize: 100,
beginAtZero: true
},
}]
},
onClick: function(evt) {
let chartInstance = chartReference.current.chartInstance
let x, y, dataIndex;
x = chartInstance.scales["x-axis-0"].getValueForPixel(evt.clientX-chartInstance.canvas.getBoundingClientRect().left)
y = chartInstance.scales["y-axis-0"].getValueForPixel(evt.clientY-chartInstance.canvas.getBoundingClientRect().top)
// 対象の範囲内の時のみ値を変える
if (x>=0 && x<props.labels.length){
if (y>0){ // 正の部分がクリックされた場合
dataIndex = 0
y = Math.floor(y)
y = y > chartInstance.scales["y-axis-0"].max ? chartInstance.scales["y-axis-0"].max : y
}else{ // 負の部分がクリックされた場合
dataIndex = 1
y = Math.floor(y)+1
y = y < chartInstance.scales["y-axis-0"].min ? chartInstance.scales["y-axis-0"].min : y
}
// chartInstanceを直接編集
chartInstance.data.datasets[dataIndex].data[x] = y
chartInstance.update()
props.setNewData(dataIndex, x, y) // 親側でpropsを変更させる
}
},
responsive: true,
maintainAspectRatio: false
}
}
return (
<Bar width={props.width} height={props.height}
ref={chartReference}
data={data()} options={optionsChartInput()}/>
)
}
このようにクリックされた位置のyの正負に応じてdatasetsのインデックスを切り替えて値をセットするという実装を行ったところ、下記のように、両方のデータセットに同じ値がセットされるという挙動になってしまいました。
// 修正後
<Bar redraw width={props.width} height={props.height}
ref={chartReference}
data={data()} options={optionsChartInput()} />
コメント