This is a Livecoding Recap β an almost-weekly post about interesting things discovered while livecoding. Usually shorter than 500 words. Often with pictures. Livecoding happens almost every Sunday at 2pm PDT on multiple channels. You should subscribe to my YouTube channel to catch me live.
I'm working on a React & D3 library that I've been thinking about for two years. My goal is to build simple-react-d3, a React and D3 library that never gets in your way.
Most libraries give you composable and reusable charting components that are easy to use and quick to get started with. Theyβre great for simple charts.
But more often than not, the more custom you want your dataviz to be, the more control you need. You begin to fight your library.
VX comes closest to the ideal get-out-of-your-way library, and even VX when I recommended it to a friend it took all of 10 minutes for him to hit the wall. "Wtf how do I do this? The library is fighting me"
π
The best way to get started was to generalize the D3Blackbox
pattern I developed for React+D3. It's the easiest and quickest way to render a random piece of D3 code in your React project.
Here's an example
Take some D3 code, wrap it in a function, pass it to a HOC (higher order component), and you're done. The HOC renders an anchor element, and your render function uses D3 to take over and manipulate the DOM.
This approach doesn't give you all the benefits of React, and it doesn't scale very well. It's meant for simple components, quick hack jobs, or when you have a lot of existing D3 code you want to use. It is the easiest and quickest way to translate any random D3 example to React.
Turning that into a library was pretty easy π
$ nwb new react-component simple-react-d3
<copy pasta>
Boom. Library.
But what if you're the kind of person who doesn't like HOCs? Maybe you prefer render props or function-as-children?
A React component that supports all reuse patterns
"Can we make this HOC work as not a HOC too? What if it supported it all popular React patterns for reuse"
I came up with this
On a scale of 1 to 10, how terrible is this #react pattern? @dan_abramov how much would this mess with stuff from 16.3? pic.twitter.com/xhsogX6G3J
β Swizec Teller (@Swizec) March 26, 2018
It's a function, SVGBlackbox
, that takes a function as argument and acts like a HOC. Pass in a func, get a component back. Just like the D3Blackbox
example above.
You can also use it directly as <SVGBlackbox />
. If you do that, you can either pass a render
prop, or a function-as-children. Both get a reference to the anchor element as their sole argument.
Now you can use the blackbox approach any way you like.
const Axis = SVGBlackbox(function () {
const scale = d3.scaleLinear().domain([0, 10]).range([0, 200])
const axis = d3.axisBottom(scale)
d3.select(this.refs.anchor).call(axis)
})
class Demo extends Component {
render() {
return (
<div>
<h1>simple-react-d3 demo</h1>
<svg width="300" height="200">
<axis x={10} y={10}></axis>
<svgblackbox x={10} y={50}>
{(anchor) => {
const scale = d3.scaleLinear().domain([0, 10]).range([0, 200])
const axis = d3.axisBottom(scale)
d3.select(anchor).call(axis)
}}
</svgblackbox>
</svg>
</div>
)
}
}
That renders two axes. One above the other.
Neat.
Unfortunately, the internet told me this is a terrible pattern, and I should feel bad. The window.requestAnimationFrame
part can lead to all sorts of problems and likely clashes with the future we're getting in React 16.3.
However, Sophie Alpert had some good suggestions π
You need the rAF call to be triggered in componentDidMount (maybe without the rAF). Otherwise it might be called before the node even exists (or while it is in the middle of an update).
β sophie alpert (@sophiebits) March 26, 2018
Make it a class? You can also return a child component that is a class. Itβs also possible to do this logic in a ref callback but thatβs a little obtuse.
β sophie alpert (@sophiebits) March 26, 2018
The idea of shoving render prop stuff into the ref
callback smells like black magic. It's so crazy that it might just work.
The ultimate reusable component
Another livecoding session later, we did it: The ultimate reusable component. You can use it as a HOC, with render-props, or function-as-children.
Less than 2 minutes to grab a random D3 example from the internet and render it in a React app. Still blackbox, but works really well.
It took 5 minutes because we had to update code from D3v4 to D3v5 and add some prop passing to SVGBlackbox
to make it easier to use.
You can see full SVGBlackbox code on GitHub. Here's how the interesting part works:
When used as a HOC, it takes your argument as the render function and passes it into the usual D3Blackbox HOC. Wires up invocation as required and hands control over to you.
When used as a component, the argument is a props
object. Take out children
and render
, store the rest as props
. Take x
and y
from that.
Then return a <g>
element moved into (x, y)
position and given all the other props. This can be handy.
Now the tricky part: A ref
callback that invokes your render function. That's right, you can hand over control of the anchor element in the ref
callback.
This works, but trips up on React's caveat about ref callbacks sometimes.
If the ref callback is defined as an inline function, it will get called twice during updates, first with null and then again with the DOM element. This is because a new instance of the function is created with each render, so React needs to clear the old ref and set up the new one.
Hence the callback is wrapped in a conditional to check that anchor
is defined.
Still feels a little dirty, but much better than the requestAnimationFrame
approach. Shouldn't mess with async stuff in React 16.3 either, I think.
Next step?
Something similar for the full feature integration where D3 calculates your props and React renders your dataviz. You can see the first part of that towards the end of the 2nd video above.
It's all coming together :)
Continue reading about Livecoding Recap: A new more versatile React pattern
Semantically similar articles hand-picked by GPT-4
- Easy D3 blackbox components with React hooks
- Announcing D3blackbox and useD3
- Tiny React & D3 flamegraph tutorial
- Livecoding #34: A Map of Global Migrations, Part 3
- Livecoding #12: towards animating 10k+ elements with React
Learned something new?
Read more Software Engineering Lessons from Production
I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.
Subscribe below π
Software Engineering Lessons from Production
Join Swizec's Newsletter and get insightful emails π on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.
"Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. π"
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help π swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers π ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are β€οΈ