Just a quick example of how I use React + d3 now a days.
| <!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