Hello everybody!
I am trying to visualize a dataset and building a multiple line graph with animation for it. I want to trigger each line in the graph being drawn programmatically (e.g with an external cell controlling which line is shown). One thing I’m trying to avoid is the whole cell being re drawn on every update in the external cell.
Any Ideas on whats the best way to go about this?
Thanks,
Itay.
You can avoid redraws by reusing the cell’s previous value via this
. Here is a basic two-cell example, with the second cell continuously appending the first cell’s values to its content:
counter = {
let n = 1;
while(true) {
yield n++;
await Promises.delay(1000);
}
}
{
if(!this) return html`<span>${counter}`;
this.appendChild(html`, ${counter}`);
return this;
}
Additional data (or APIs) can be attached to the DOM element for reuse.
I don’t recommend using this
; it’s too easy to get wrong. (I appreciate you replying to this question though, @mootari!)
My recommendation is to use an update (side-effect) cell that calls a function on the primary (chart) cell. This technique is described and demonstrated here:
@mbostock Just to deepen my understanding: Why are side effect cells preferable over this
, even though the implicit dependencies they introduce cannot be detected by the runtime, and are thus not processed e.g. for imports?
Is it because a cell that processes this
can take over control, making it harder to trigger a reset?
1 Like
Great question!
When you use this
, you commit to updating incrementally regardless of what changed. Yet in practice, you tend to thinking about one specific thing changing — say a counter, or a dataset. Whereas when you use a side-effect cell, that cell is reacting only to the specific thing (its inputs); if anything else changes, the whole chart is redrawn from scratch (the non-incremental way).
In short, with a side-effect cell, you opt-in to specific incremental changes; with this
, you must handle anything changing.
For example, say you use const canvas = this || DOM.canvas(width, height)
to avoid creating a new canvas element and 2D context on redraw. In the normal case, you’re probably fine. But if the width or height ever changes, oops, you’ll have the wrong size canvas. (Some of the early D3 examples had this problem, and weren’t responsive as a result.)
A subtlety with the method I recommend is that the update function should be defined in the chart cell: the side-effect cell should only call the exposed update function, not reach into the chart and modify it directly. This way as you edit the update function, you’re invalidating the chart cell and the whole thing is redrawn from scratch. This avoids getting into a nondeterministic state from earlier versions of the code. Again, you’re opting-in minimally to just the thing you expect to change, and letting any other change happen through normal dataflow.
6 Likes
Thanks @mbostock and @mootari for the replies and interesting comments.
I ended up implementing the update (side-effect) approach! thanks for attaching the example, just what I needed to get unstuck!
1 Like