以下のような Component を作る。
import React, { Component, PropTypes } from 'react';
export default class CanvasComponent extends Component {
componentDidMount() {
this.updateCanvas();
}
componentWillReceiveProps(nextProps) {
if (this.props !== nextProps) {
this.updateCanvas();
}
}
componentDidUpdate() {
this.updateCanvas();
}
updateCanvas() {
const { canvas } = this;
const context = canvas.getContext('2d');
this.props.updateCanvas(context);
}
render() {
return <canvas ref={(e) => { this.canvas = e; }} width={this.props.width} height={this.props.height}></canvas>;
}
}
CanvasComponent.propTypes = {
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
updateCanvas: PropTypes.func.isRequired,
};
で、呼び出し元の updateCanvas()
メソッドの中で Canvas の描画処理を書く。
import React, { Component } from 'react';
import CanvasComponent from './CanvasComponent';
const CANVAS_WIDTH = 140;
const CANVAS_HEIGHT = 30;
export default class TestComponent extends Component {
constructor() {
this.state = {
text: 'text',
};
}
render() {
const canvasProps = {
width: CANVAS_WIDTH,
height: CANVAS_HEIGHT,
updateCanvas: (context) => {
context.fillStyle = 'red';
context.font = '24px Arial';
context.textAlign = 'left';
context.textBaseline = 'top';
context.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
context.fillText(this.state.text, 0, 0);
},
};
return (
<div>
<input value={this.state.text} onChange={elm => this.setState({text: elm.target.value})} />
<CanvasComponent {...canvasProps} />
</div>
);
}
}
つまり、CanvasComponent
のなかで更新があるか、Props に更新があった時に Canvas の描画処理を走らせれば良い。
なお、この手法は Canvas-Element に限った話ではなく、例えば jQuery-Plugin でも下記のようにすれば React で扱うことができる。
import React, { Component, PropTypes } from 'react';
import jQuery from 'jquery';
import 'fullcalendar';
export default class FullCalendar extends Component {
componentDidMount() {
const { calendar } = this.refs;
jQuery(calendar).fullCalendar({
timezone: 'JST',
timeFormat: 'YYYY/M/D H:mm',
header: {
left: 'today prev next',
center: 'title',
right: 'month agendaWeek agendaDay',
},
firstDay: 1,
eventClick: event => {
if (typeof this.props.onClickEvent == 'function') {
this.props.onClickEvent(event);
}
},
dayClick: date => {
jQuery(calendar).fullCalendar('changeView', 'agendaDay');
jQuery(calendar).fullCalendar('gotoDate', date);
},
});
}
componentWillReceiveProps(nextProps) {
const { calendar } = this.refs;
if (nextProps.events !== this.props.events) {
jQuery(calendar).fullCalendar('removeEvents');
jQuery(calendar).fullCalendar('addEventSource', nextProps.events);
}
}
componentWillUnmount() {
const { calendar } = this.refs;
jQuery(calendar).fullCalendar('destroy');
}
render() {
return <div ref="calendar"></div>;
}
}
FullCalendar.propTypes = {
events: PropTypes.array,
onClickEvent: PropTypes.func,
};