Just a quick example of how I use React + d3 now a days.
Last active
June 29, 2016 20:03
-
-
Save milroc/aacdb75156d51d9dbe4d to your computer and use it in GitHub Desktop.
A quick React + D3 sparkline
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<script src="https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-xpa1/t39.3284-6/12512178_218562685145124_130271029_n.js"></script> | |
<script src="https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-xfa1/t39.3284-6/12512184_1664789273772979_614489084_n.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.js"></script> | |
</head> | |
<body> | |
<div id='example'></div> | |
<script type="text/babel"> | |
class Sparkline extends React.Component { | |
constructor(props) { | |
super(props); | |
this.xScale = d3.scale.linear(); | |
this.yScale = d3.scale.linear(); | |
this.line = d3.svg.line(); | |
this._updateDataTransforms(props); | |
} | |
componentDidMount() { | |
const self = this; | |
d3.select(ReactDOM.findDOMNode(this.refs.svg)) | |
.on('mousemove', function() { self._onMouseMove(d3.mouse(this)[0]); }) | |
.on('mouseleave', function() { self._onMouseMove(null); }); | |
} | |
componentWillReceiveNewProps(newProps) { | |
this._updateDataTransforms(newProps); | |
} | |
_updateDataTransforms(props) { | |
const {xAccessor, yAccessor, width, height, data} = props; | |
this.xScale | |
.domain([0, data.length]) | |
.range([0, width]); | |
this.yScale | |
.domain([0, 10]) | |
.range([height, 0]); | |
this.line | |
.x((d, i) => this.xScale(xAccessor(d, i))) | |
.y((d, i) => this.yScale(yAccessor(d, i))); | |
this.bisectByX = d3.bisector(xAccessor).left; | |
} | |
_onMouseMove(xPixelPos) { | |
const {data, onHover} = this.props; | |
if (xPixelPos === null) { | |
onHover(null, null); | |
} | |
else { | |
const xValue = this.xScale.invert(xPixelPos); | |
const i = this.bisectByX(data, xValue, 1); | |
onHover(data[i], i); | |
} | |
} | |
render() { | |
const {data, width, height, xAccessor, hovered} = this.props; | |
const hoveredRender = (hovered) | |
? ( | |
<line | |
x1={this.xScale(xAccessor(hovered))} | |
x2={this.xScale(xAccessor(hovered))} | |
y0={0} | |
y1={height} | |
style={{strokeWidth: '0.5px', stroke: 'steelblue'}} | |
/> | |
) | |
: null; | |
return ( | |
<svg width={width} height={height} ref="svg"> | |
<path | |
style={{fill: 'none', strokeWidth: '0.5px', stroke: 'steelblue'}} | |
d={this.line(data)} | |
/> | |
{hoveredRender} | |
</svg> | |
); | |
} | |
} | |
Sparkline.defaultProps = { | |
xAccessor: ({x}) => x, | |
yAccessor: ({y}) => y, | |
}; | |
class Example extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
hovered: null, | |
}; | |
} | |
render() { | |
const {data} = this.props; | |
const {hovered} = this.state; | |
const value = (hovered) | |
? hovered.y | |
: data.reduce((s, {y}) => s + y, 0); // total | |
return ( | |
<div> | |
<Sparkline | |
data={data} | |
width={100} | |
height={20} | |
hovered={hovered} | |
onHover={(hovered, index) => this.setState({hovered})} | |
/> | |
{value} | |
</div> | |
); | |
} | |
} | |
let data = [3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 3, 6, 3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9]; | |
// Interesting fact: d3.bisect accessors assume your not bisecting by the index. | |
// Duh... | |
data = data.map((y, x) => { return {x, y}; }); | |
ReactDOM.render( | |
<Example data={data} />, | |
document.getElementById('example') | |
); | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment