Skip to content

Instantly share code, notes, and snippets.

@curran
Last active August 19, 2023 14:00
Show Gist options
  • Save curran/a6c261aca1a12452111cb1b797c04d70 to your computer and use it in GitHub Desktop.
Save curran/a6c261aca1a12452111cb1b797c04d70 to your computer and use it in GitHub Desktop.
React & D3 Starter

Exported from VizHub: React & D3 Starter.

This viz is a starter that demonstrates patterns used in real world projects in which complexity is anticipated to scale.

  • Decoupled React & D3 integration
  • Opening moves for scalable complexity
  • Industry standard ES modules and JSX
  • Copy-paste into existing projects

Any questions? Ask on Twitter!

import {useData} from './useData';
import {VizWrapper} from './VizWrapper';
export const App = () => {
const data = useData();
return data ? (
<VizWrapper data={data} />
) : (
// Could be expanded with a real loading indicator.
'Loading...'
);
};
import { axisLeft, axisBottom } from 'd3';
export const axes = (
selection,
{ height, margin, xScale, yScale }
) => {
const { left, bottom } = margin;
selection
.selectAll('g.axis-y')
.data([null])
.join('g')
.attr('class', 'axis axis-y')
.attr('transform', `translate(${left},0)`)
.call(axisLeft(yScale));
selection
.selectAll('g.axis-x')
.data([null])
.join('g')
.attr('class', 'axis axis-x')
.attr(
'transform',
`translate(0,${height - bottom})`
)
.call(axisBottom(xScale));
};
(function (react, d3, ReactDOM) {
'use strict';
ReactDOM = ReactDOM && Object.prototype.hasOwnProperty.call(ReactDOM, 'default') ? ReactDOM['default'] : ReactDOM;
const parseRow = (d) => {
d.sepal_length = +d.sepal_length;
d.sepal_width = +d.sepal_width;
d.petal_length = +d.petal_length;
d.petal_width = +d.petal_width;
return d;
};
const useData = (outputPath) => {
const [data, setData] = react.useState(null);
react.useEffect(async () => {
setData(await d3.csv('data.csv', parseRow));
}, []);
return data;
};
const axes = (
selection,
{ height, margin, xScale, yScale }
) => {
const { left, bottom } = margin;
selection
.selectAll('g.axis-y')
.data([null])
.join('g')
.attr('class', 'axis axis-y')
.attr('transform', `translate(${left},0)`)
.call(d3.axisLeft(yScale));
selection
.selectAll('g.axis-x')
.data([null])
.join('g')
.attr('class', 'axis axis-x')
.attr(
'transform',
`translate(0,${height - bottom})`
)
.call(d3.axisBottom(xScale));
};
const viz = (
selection,
{
data,
xValue,
yValue,
width,
height,
margin,
circleRadius,
}
) => {
const { top, right, bottom, left } = margin;
const xScale = d3.scaleLinear()
.domain(d3.extent(data, xValue))
.range([left, width - right]);
const yScale = d3.scaleLinear()
.domain(d3.extent(data, yValue))
.range([height - bottom, top]);
selection
.selectAll('circle')
.data(data)
.join('circle')
.attr('r', circleRadius)
.attr('fill-opacity', 0.5)
.attr('cx', (d) => xScale(xValue(d)))
.attr('cy', (d) => yScale(yValue(d)));
axes(selection, {
height,
margin,
xScale,
yScale,
});
};
const VizWrapper = ({ data }) => {
const ref = react.useRef();
const width = window.innerWidth;
const height = window.innerHeight;
react.useEffect(() => {
viz(d3.select(ref.current), {
data,
xValue: (d) => d.sepal_length,
yValue: (d) => d.petal_length,
width,
height,
margin: {
top: 20,
right: 20,
bottom: 40,
left: 40,
},
circleRadius: 10,
});
}, [data]);
return (
React.createElement( 'svg', {
width: width, height: height, ref: ref })
);
};
const App = () => {
const data = useData();
return data ? (
React.createElement( VizWrapper, { data: data })
) : (
// Could be expanded with a real loading indicator.
'Loading...'
);
};
ReactDOM.render(
React.createElement( App, { outputPath: "" }),
document.getElementById('root')
);
}(React, d3, ReactDOM));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzIjpbInVzZURhdGEuanMiLCJheGVzLmpzIiwidml6LmpzIiwiVml6V3JhcHBlci5qcyIsIkFwcC5qcyIsImluZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHVzZVN0YXRlLCB1c2VFZmZlY3QgfSBmcm9tICdyZWFjdCc7XG5pbXBvcnQgeyBjc3YgfSBmcm9tICdkMyc7XG5cbmNvbnN0IHBhcnNlUm93ID0gKGQpID0+IHtcbiAgZC5zZXBhbF9sZW5ndGggPSArZC5zZXBhbF9sZW5ndGg7XG4gIGQuc2VwYWxfd2lkdGggPSArZC5zZXBhbF93aWR0aDtcbiAgZC5wZXRhbF9sZW5ndGggPSArZC5wZXRhbF9sZW5ndGg7XG4gIGQucGV0YWxfd2lkdGggPSArZC5wZXRhbF93aWR0aDtcbiAgcmV0dXJuIGQ7XG59O1xuXG5leHBvcnQgY29uc3QgdXNlRGF0YSA9IChvdXRwdXRQYXRoKSA9PiB7XG4gIGNvbnN0IFtkYXRhLCBzZXREYXRhXSA9IHVzZVN0YXRlKG51bGwpO1xuXG4gIHVzZUVmZmVjdChhc3luYyAoKSA9PiB7XG4gICAgc2V0RGF0YShhd2FpdCBjc3YoJ2RhdGEuY3N2JywgcGFyc2VSb3cpKTtcbiAgfSwgW10pO1xuXG4gIHJldHVybiBkYXRhO1xufTtcbiIsImltcG9ydCB7IGF4aXNMZWZ0LCBheGlzQm90dG9tIH0gZnJvbSAnZDMnO1xuXG5leHBvcnQgY29uc3QgYXhlcyA9IChcbiAgc2VsZWN0aW9uLFxuICB7IGhlaWdodCwgbWFyZ2luLCB4U2NhbGUsIHlTY2FsZSB9XG4pID0+IHtcbiAgY29uc3QgeyBsZWZ0LCBib3R0b20gfSA9IG1hcmdpbjtcblxuICBzZWxlY3Rpb25cbiAgICAuc2VsZWN0QWxsKCdnLmF4aXMteScpXG4gICAgLmRhdGEoW251bGxdKVxuICAgIC5qb2luKCdnJylcbiAgICAuYXR0cignY2xhc3MnLCAnYXhpcyBheGlzLXknKVxuICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBgdHJhbnNsYXRlKCR7bGVmdH0sMClgKVxuICAgIC5jYWxsKGF4aXNMZWZ0KHlTY2FsZSkpO1xuXG4gIHNlbGVjdGlvblxuICAgIC5zZWxlY3RBbGwoJ2cuYXhpcy14JylcbiAgICAuZGF0YShbbnVsbF0pXG4gICAgLmpvaW4oJ2cnKVxuICAgIC5hdHRyKCdjbGFzcycsICdheGlzIGF4aXMteCcpXG4gICAgLmF0dHIoXG4gICAgICAndHJhbnNmb3JtJyxcbiAgICAgIGB0cmFuc2xhdGUoMCwke2hlaWdodCAtIGJvdHRvbX0pYFxuICAgIClcbiAgICAuY2FsbChheGlzQm90dG9tKHhTY2FsZSkpO1xufTtcbiIsImltcG9ydCB7IHNjYWxlTGluZWFyLCBleHRlbnQgfSBmcm9tICdkMyc7XG5pbXBvcnQgeyBheGVzIH0gZnJvbSAnLi9heGVzJztcblxuZXhwb3J0IGNvbnN0IHZpeiA9IChcbiAgc2VsZWN0aW9uLFxuICB7XG4gICAgZGF0YSxcbiAgICB4VmFsdWUsXG4gICAgeVZhbHVlLFxuICAgIHdpZHRoLFxuICAgIGhlaWdodCxcbiAgICBtYXJnaW4sXG4gICAgY2lyY2xlUmFkaXVzLFxuICB9XG4pID0+IHtcbiAgY29uc3QgeyB0b3AsIHJpZ2h0LCBib3R0b20sIGxlZnQgfSA9IG1hcmdpbjtcblxuICBjb25zdCB4U2NhbGUgPSBzY2FsZUxpbmVhcigpXG4gICAgLmRvbWFpbihleHRlbnQoZGF0YSwgeFZhbHVlKSlcbiAgICAucmFuZ2UoW2xlZnQsIHdpZHRoIC0gcmlnaHRdKTtcblxuICBjb25zdCB5U2NhbGUgPSBzY2FsZUxpbmVhcigpXG4gICAgLmRvbWFpbihleHRlbnQoZGF0YSwgeVZhbHVlKSlcbiAgICAucmFuZ2UoW2hlaWdodCAtIGJvdHRvbSwgdG9wXSk7XG5cbiAgc2VsZWN0aW9uXG4gICAgLnNlbGVjdEFsbCgnY2lyY2xlJylcbiAgICAuZGF0YShkYXRhKVxuICAgIC5qb2luKCdjaXJjbGUnKVxuICAgIC5hdHRyKCdyJywgY2lyY2xlUmFkaXVzKVxuICAgIC5hdHRyKCdmaWxsLW9wYWNpdHknLCAwLjUpXG4gICAgLmF0dHIoJ2N4JywgKGQpID0+IHhTY2FsZSh4VmFsdWUoZCkpKVxuICAgIC5hdHRyKCdjeScsIChkKSA9PiB5U2NhbGUoeVZhbHVlKGQpKSk7XG5cbiAgYXhlcyhzZWxlY3Rpb24sIHtcbiAgICBoZWlnaHQsXG4gICAgbWFyZ2luLFxuICAgIHhTY2FsZSxcbiAgICB5U2NhbGUsXG4gIH0pO1xufTtcbiIsImltcG9ydCB7IHVzZVJlZiwgdXNlRWZmZWN0IH0gZnJvbSAncmVhY3QnO1xuaW1wb3J0IHsgc2VsZWN0IH0gZnJvbSAnZDMnO1xuaW1wb3J0IHsgdml6IH0gZnJvbSAnLi92aXonO1xuXG5leHBvcnQgY29uc3QgVml6V3JhcHBlciA9ICh7IGRhdGEgfSkgPT4ge1xuICBjb25zdCByZWYgPSB1c2VSZWYoKTtcblxuICBjb25zdCB3aWR0aCA9IHdpbmRvdy5pbm5lcldpZHRoO1xuICBjb25zdCBoZWlnaHQgPSB3aW5kb3cuaW5uZXJIZWlnaHQ7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICB2aXooc2VsZWN0KHJlZi5jdXJyZW50KSwge1xuICAgICAgZGF0YSxcbiAgICAgIHhWYWx1ZTogKGQpID0+IGQuc2VwYWxfbGVuZ3RoLFxuICAgICAgeVZhbHVlOiAoZCkgPT4gZC5wZXRhbF9sZW5ndGgsXG4gICAgICB3aWR0aCxcbiAgICAgIGhlaWdodCxcbiAgICAgIG1hcmdpbjoge1xuICAgICAgICB0b3A6IDIwLFxuICAgICAgICByaWdodDogMjAsXG4gICAgICAgIGJvdHRvbTogNDAsXG4gICAgICAgIGxlZnQ6IDQwLFxuICAgICAgfSxcbiAgICAgIGNpcmNsZVJhZGl1czogMTAsXG4gICAgfSk7XG4gIH0sIFtkYXRhXSk7XG5cbiAgcmV0dXJuIChcbiAgICA8c3ZnXG4gICAgICB3aWR0aD17d2lkdGh9XG4gICAgICBoZWlnaHQ9e2hlaWdodH1cbiAgICAgIHJlZj17cmVmfVxuICAgIC8+XG4gICk7XG59O1xuIiwiaW1wb3J0IHt1c2VEYXRhfSBmcm9tICcuL3VzZURhdGEnO1xuaW1wb3J0IHtWaXpXcmFwcGVyfSBmcm9tICcuL1ZpeldyYXBwZXInO1xuXG5leHBvcnQgY29uc3QgQXBwID0gKCkgPT4ge1xuICBjb25zdCBkYXRhID0gdXNlRGF0YSgpO1xuICByZXR1cm4gZGF0YSA/IChcbiAgICA8Vml6V3JhcHBlciBkYXRhPXtkYXRhfSAvPlxuICApIDogKFxuICAgIC8vIENvdWxkIGJlIGV4cGFuZGVkIHdpdGggYSByZWFsIGxvYWRpbmcgaW5kaWNhdG9yLlxuICAgICdMb2FkaW5nLi4uJ1xuICApO1xufTtcbiIsImltcG9ydCB7IEFwcCB9IGZyb20gJy4vQXBwJztcbmltcG9ydCBSZWFjdERPTSBmcm9tICdyZWFjdC1kb20nO1xuXG5SZWFjdERPTS5yZW5kZXIoXG4gIDxBcHAgb3V0cHV0UGF0aD1cIlwiIC8+LFxuICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncm9vdCcpXG4pO1xuIl0sIm5hbWVzIjpbInVzZVN0YXRlIiwidXNlRWZmZWN0IiwiY3N2IiwiYXhpc0xlZnQiLCJheGlzQm90dG9tIiwic2NhbGVMaW5lYXIiLCJleHRlbnQiLCJ1c2VSZWYiLCJzZWxlY3QiXSwibWFwcGluZ3MiOiI7Ozs7O0VBR0EsTUFBTSxRQUFRLEdBQUcsQ0FBQyxDQUFDLEtBQUs7RUFDeEIsRUFBRSxDQUFDLENBQUMsWUFBWSxHQUFHLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQztFQUNuQyxFQUFFLENBQUMsQ0FBQyxXQUFXLEdBQUcsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDO0VBQ2pDLEVBQUUsQ0FBQyxDQUFDLFlBQVksR0FBRyxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUM7RUFDbkMsRUFBRSxDQUFDLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQztFQUNqQyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0VBQ1gsQ0FBQyxDQUFDO0FBQ0Y7RUFDTyxNQUFNLE9BQU8sR0FBRyxDQUFDLFVBQVUsS0FBSztFQUN2QyxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsT0FBTyxD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
sepal_length sepal_width petal_length petal_width species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa
4.6 3.4 1.4 0.3 setosa
5.0 3.4 1.5 0.2 setosa
4.4 2.9 1.4 0.2 setosa
4.9 3.1 1.5 0.1 setosa
5.4 3.7 1.5 0.2 setosa
4.8 3.4 1.6 0.2 setosa
4.8 3.0 1.4 0.1 setosa
4.3 3.0 1.1 0.1 setosa
5.8 4.0 1.2 0.2 setosa
5.7 4.4 1.5 0.4 setosa
5.4 3.9 1.3 0.4 setosa
5.1 3.5 1.4 0.3 setosa
5.7 3.8 1.7 0.3 setosa
5.1 3.8 1.5 0.3 setosa
5.4 3.4 1.7 0.2 setosa
5.1 3.7 1.5 0.4 setosa
4.6 3.6 1.0 0.2 setosa
5.1 3.3 1.7 0.5 setosa
4.8 3.4 1.9 0.2 setosa
5.0 3.0 1.6 0.2 setosa
5.0 3.4 1.6 0.4 setosa
5.2 3.5 1.5 0.2 setosa
5.2 3.4 1.4 0.2 setosa
4.7 3.2 1.6 0.2 setosa
4.8 3.1 1.6 0.2 setosa
5.4 3.4 1.5 0.4 setosa
5.2 4.1 1.5 0.1 setosa
5.5 4.2 1.4 0.2 setosa
4.9 3.1 1.5 0.1 setosa
5.0 3.2 1.2 0.2 setosa
5.5 3.5 1.3 0.2 setosa
4.9 3.1 1.5 0.1 setosa
4.4 3.0 1.3 0.2 setosa
5.1 3.4 1.5 0.2 setosa
5.0 3.5 1.3 0.3 setosa
4.5 2.3 1.3 0.3 setosa
4.4 3.2 1.3 0.2 setosa
5.0 3.5 1.6 0.6 setosa
5.1 3.8 1.9 0.4 setosa
4.8 3.0 1.4 0.3 setosa
5.1 3.8 1.6 0.2 setosa
4.6 3.2 1.4 0.2 setosa
5.3 3.7 1.5 0.2 setosa
5.0 3.3 1.4 0.2 setosa
7.0 3.2 4.7 1.4 versicolor
6.4 3.2 4.5 1.5 versicolor
6.9 3.1 4.9 1.5 versicolor
5.5 2.3 4.0 1.3 versicolor
6.5 2.8 4.6 1.5 versicolor
5.7 2.8 4.5 1.3 versicolor
6.3 3.3 4.7 1.6 versicolor
4.9 2.4 3.3 1.0 versicolor
6.6 2.9 4.6 1.3 versicolor
5.2 2.7 3.9 1.4 versicolor
5.0 2.0 3.5 1.0 versicolor
5.9 3.0 4.2 1.5 versicolor
6.0 2.2 4.0 1.0 versicolor
6.1 2.9 4.7 1.4 versicolor
5.6 2.9 3.6 1.3 versicolor
6.7 3.1 4.4 1.4 versicolor
5.6 3.0 4.5 1.5 versicolor
5.8 2.7 4.1 1.0 versicolor
6.2 2.2 4.5 1.5 versicolor
5.6 2.5 3.9 1.1 versicolor
5.9 3.2 4.8 1.8 versicolor
6.1 2.8 4.0 1.3 versicolor
6.3 2.5 4.9 1.5 versicolor
6.1 2.8 4.7 1.2 versicolor
6.4 2.9 4.3 1.3 versicolor
6.6 3.0 4.4 1.4 versicolor
6.8 2.8 4.8 1.4 versicolor
6.7 3.0 5.0 1.7 versicolor
6.0 2.9 4.5 1.5 versicolor
5.7 2.6 3.5 1.0 versicolor
5.5 2.4 3.8 1.1 versicolor
5.5 2.4 3.7 1.0 versicolor
5.8 2.7 3.9 1.2 versicolor
6.0 2.7 5.1 1.6 versicolor
5.4 3.0 4.5 1.5 versicolor
6.0 3.4 4.5 1.6 versicolor
6.7 3.1 4.7 1.5 versicolor
6.3 2.3 4.4 1.3 versicolor
5.6 3.0 4.1 1.3 versicolor
5.5 2.5 4.0 1.3 versicolor
5.5 2.6 4.4 1.2 versicolor
6.1 3.0 4.6 1.4 versicolor
5.8 2.6 4.0 1.2 versicolor
5.0 2.3 3.3 1.0 versicolor
5.6 2.7 4.2 1.3 versicolor
5.7 3.0 4.2 1.2 versicolor
5.7 2.9 4.2 1.3 versicolor
6.2 2.9 4.3 1.3 versicolor
5.1 2.5 3.0 1.1 versicolor
5.7 2.8 4.1 1.3 versicolor
6.3 3.3 6.0 2.5 virginica
5.8 2.7 5.1 1.9 virginica
7.1 3.0 5.9 2.1 virginica
6.3 2.9 5.6 1.8 virginica
6.5 3.0 5.8 2.2 virginica
7.6 3.0 6.6 2.1 virginica
4.9 2.5 4.5 1.7 virginica
7.3 2.9 6.3 1.8 virginica
6.7 2.5 5.8 1.8 virginica
7.2 3.6 6.1 2.5 virginica
6.5 3.2 5.1 2.0 virginica
6.4 2.7 5.3 1.9 virginica
6.8 3.0 5.5 2.1 virginica
5.7 2.5 5.0 2.0 virginica
5.8 2.8 5.1 2.4 virginica
6.4 3.2 5.3 2.3 virginica
6.5 3.0 5.5 1.8 virginica
7.7 3.8 6.7 2.2 virginica
7.7 2.6 6.9 2.3 virginica
6.0 2.2 5.0 1.5 virginica
6.9 3.2 5.7 2.3 virginica
5.6 2.8 4.9 2.0 virginica
7.7 2.8 6.7 2.0 virginica
6.3 2.7 4.9 1.8 virginica
6.7 3.3 5.7 2.1 virginica
7.2 3.2 6.0 1.8 virginica
6.2 2.8 4.8 1.8 virginica
6.1 3.0 4.9 1.8 virginica
6.4 2.8 5.6 2.1 virginica
7.2 3.0 5.8 1.6 virginica
7.4 2.8 6.1 1.9 virginica
7.9 3.8 6.4 2.0 virginica
6.4 2.8 5.6 2.2 virginica
6.3 2.8 5.1 1.5 virginica
6.1 2.6 5.6 1.4 virginica
7.7 3.0 6.1 2.3 virginica
6.3 3.4 5.6 2.4 virginica
6.4 3.1 5.5 1.8 virginica
6.0 3.0 4.8 1.8 virginica
6.9 3.1 5.4 2.1 virginica
6.7 3.1 5.6 2.4 virginica
6.9 3.1 5.1 2.3 virginica
5.8 2.7 5.1 1.9 virginica
6.8 3.2 5.9 2.3 virginica
6.7 3.3 5.7 2.5 virginica
6.7 3.0 5.2 2.3 virginica
6.3 2.5 5.0 1.9 virginica
6.5 3.0 5.2 2.0 virginica
6.2 3.4 5.4 2.3 virginica
5.9 3.0 5.1 1.8 virginica
<!DOCTYPE html><html><head>
<title>React &amp; D3 Starter</title>
<link rel="stylesheet" href="styles.css">
<script src="https://unpkg.com/d3@7.4.4/dist/d3.min.js"></script><script src="https://unpkg.com/react@18.1.0/umd/react.production.min.js"></script><script src="https://unpkg.com/react-dom@18.1.0/umd/react-dom.production.min.js"></script></head>
<body>
<div id="root"></div>
<script src="bundle.js"></script></body></html>
import { App } from './App';
import ReactDOM from 'react-dom';
ReactDOM.render(
<App outputPath="" />,
document.getElementById('root')
);
{
"scripts": {
"build": "rollup -c"
},
"devDependencies": {
"rollup": "latest",
"@rollup/plugin-buble": "latest"
}
}
const buble = require('@rollup/plugin-buble');
export default {
input: 'index.js',
external: ["d3","react","react-dom"],
output: {
file: 'bundle.js',
format: 'iife',
sourcemap: true,
globals: {"d3":"d3","react":"React","react-dom":"ReactDOM"}
},
plugins: [buble()]
};
body {
margin: 0;
overflow: hidden;
}
.message {
font-size: 13em;
text-align: center;
}
import { useState, useEffect } from 'react';
import { csv } from 'd3';
const parseRow = (d) => {
d.sepal_length = +d.sepal_length;
d.sepal_width = +d.sepal_width;
d.petal_length = +d.petal_length;
d.petal_width = +d.petal_width;
return d;
};
export const useData = (outputPath) => {
const [data, setData] = useState(null);
useEffect(async () => {
setData(await csv('data.csv', parseRow));
}, []);
return data;
};
import { scaleLinear, extent } from 'd3';
import { axes } from './axes';
export const viz = (
selection,
{
data,
xValue,
yValue,
width,
height,
margin,
circleRadius,
}
) => {
const { top, right, bottom, left } = margin;
const xScale = scaleLinear()
.domain(extent(data, xValue))
.range([left, width - right]);
const yScale = scaleLinear()
.domain(extent(data, yValue))
.range([height - bottom, top]);
selection
.selectAll('circle')
.data(data)
.join('circle')
.attr('r', circleRadius)
.attr('fill-opacity', 0.5)
.attr('cx', (d) => xScale(xValue(d)))
.attr('cy', (d) => yScale(yValue(d)));
axes(selection, {
height,
margin,
xScale,
yScale,
});
};
import { useRef, useEffect } from 'react';
import { select } from 'd3';
import { viz } from './viz';
export const VizWrapper = ({ data }) => {
const ref = useRef();
const width = window.innerWidth;
const height = window.innerHeight;
useEffect(() => {
viz(select(ref.current), {
data,
xValue: (d) => d.sepal_length,
yValue: (d) => d.petal_length,
width,
height,
margin: {
top: 20,
right: 20,
bottom: 40,
left: 40,
},
circleRadius: 10,
});
}, [data]);
return (
<svg
width={width}
height={height}
ref={ref}
/>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment