Performant way to update scales?
See original GitHub issueIām trying to make a chart where the scaleās domain is adjustable through a slider (input[type=ārangeā]) beneath the chart. (Like this, for example.)
The problem that I am running into is that using setState is not performant, as I would like updates to happen every few ms as the slider is dragged, and not just about once per second.
With setState, Iād do something like this:
onRangeChange(e) {
const newMaxDays = // calculation
this.setState({ maxDays: newMaxDays })
}
render() {
data = someData()
const xScale = scalePower({
range: [0, width],
domain: [0, this.state.maxDays],
exponent: 0.5
})
return (
<div>
<svg>
<LinePath
data={data}
x={(d) => xScale(d.index)}
y={(d) => yScale(d.price)}
/>
</svg>
<input
type="range"
min={0}
max={100}
defaultValue={50}
onChange={(e) => this.onRangeChange(e)}
/>
</div>
)
This example ^ is slow and laggy. As the user drags the range slider, the chart scale updates, but it is very choppy.
The more performant alternative is to use the innerRef
property on the line path so I can access it directly, and then use native d3 logic to set the scale. But Iām having trouble figuring out how to do this.
constructor(props) {
this.lineRef = React.createRef();
this.xScale = null
this.yScale = null
}
onRangeChange(e) {
const newMaxDays = // calculation
// TODO: apply the updated xScale to the view... but how??
// The following doesn't work...
this.xScale.domain([0, newMaxDays])
const line = d3.line()
.x(d => this.xScale(d.index))
.y(d => this.yScale(d.price))
d3.select(this.lineRef.current)
.attr('d', line)
}
render() {
data = someData()
this.xScale = scalePower({
range: [0, width],
domain: [0, maxDays],
exponent: 0.5
})
this.yScale = ...
return (
<div>
<svg>
<LinePath
innerRef={this.lineRef}
data={data}
x={(d) => this.xScale(d.index)}
y={(d) => this.yScale(d.price)}
/>
</svg>
<input
type="range"
min={0}
max={100}
defaultValue={50}
onChange={(e) => this.onRangeChange(e)}
/>
</div>
)
Any tips or is there a different preferred way to do this?
Issue Analytics
- State:
- Created 4 years ago
- Comments:6
Top GitHub Comments
Hi @cilphex š thanks for checking out
vx
. Perf in react land can indeed be a tricky thing. Your solution does mix mental models of d3 vs react manipulating the dom, which is why we createdvx
so I hope we can get a āpure vx/reactā solution š (and I thiiiink that your approach might result in subtle bugs because once you modify thed
attribute of the ref, thedom
is actually different from what react thinks it is).Iām curious if a āpure
react
ā version like the following would work, basically you are using thechildren
render function for full control over theLinePath
rendering and to get access to theline
/path generator, and only updating the scales + domain when needed (note I havenāt tested this, just wrote it up so might be errors I didnāt catch). It may still re-initialize theline
, but might be worth a shot to try and leverage thechildren
render function override.@williaster I think
LinePath.children
is missing from the documentation?Also curious how you would apply this solution to the
Grid
component. It also has nochildren
property according to the documentation. (But now Iām not sure if thatās because the documentation is lacking or if it really doesnāt.)