D3 in React in Streamlit

Hellow there,

As a React/D3/Typescript beginner, I have mixed feelings about how I built the following demo, which takes a randomized array in Python that changes at every reload, passes it to the React component which builds a D3 chart over it with transitions :

demo

Itā€™s mostly Simple D3 with React Hooks. React Function Components with Hooksā€¦ | by Jeff Butsch | Medium with a slice of Amelia Wattenberger. I like the React Streamlit class since it handles all the render events / data fetch for me, but from reading the latter article, Iā€™m under the impression that itā€™s preferable if D3 does not mutate the DOM through React ref, so either like in the article I declaratively rebuild animations/transitions (through react-spring for example) and donā€™t use the D3 functions which mutate DOM, or drop the React Streamlit class in favor of the plain TS approach.

There are plenty other articles which debate over this D3 integration in React, so I guess my main questions are :

  • how would you do D3 in Streamlit ?
  • do the pros for using React Streamlit class outweigh the cons of having D3 clash with React ?
4 Likes

Well for those interested, I decided to go Streamlit with React Hooks. D3.js with useRef + useEffect works well enough in a small custom component :slight_smile: and then I tried to cram every bit of D3 in the demo.

Some notes or things I noticed, also related to what @danthe3rd noticed in Re-sending props on load/resize :

  • there seems to be rerenders on resize (I know because d3 greys the line on data update which I thought only happens on prop change, but when I resize the line greys out). Is there a resizeObserver somewhere in Streamlit ?

  • Also noticed, on first load there seems to be 2 renders because on first load, circles are created then transition from null radius to full size radius, but in the demo below on first load the circles donā€™t fully appear, theyā€™re stuck with a radius of 0.003 because thereā€™s a second rerender just after the first load, during the transition that stops the growth radius transition. Easy to workaround, just also update radius in rerender no worries. And then when I rerun the app and randomize the data the circles fully appear now, no block in transition, so when app reruns thereā€™s only 1 render. I know Iā€™m supposed to debounce but that was unexpected :slight_smile:

The full code is there if you want to do D3 on Streamlit ! https://github.com/andfanilo/streamlit-d3-demo.

4 Likes

This is amazing work and I wish I had the time to contribute.

Given D3ā€™s record of getting all up in Reactā€™s DOM I am wondering about investing my time in Vega Lite with React rather than D3.

Has this work sparked any thoughts on that?

investing my time in Vega Lite

Streamlit already supports vega-lite via Altair (st.altair_chart()), so not sure thereā€™s much open space here to work on.

Thereā€™s even a st.vega_lite_chart :confused: . You could try your hands with Vega in React to build plots not available in Vegalite like network or tree diagrams but thatā€™s really it on the Vega ecosystem side.

I pondered the question a lot, I have a list of 20+ URLs giving arguments and counterarguments for D3 in React :laughing:, but React hooks really eases the React/D3 friction a little bit (also thanks to this video series on D3 with React hooks and talk by Monica Wojciechowska).
Then again you can also build your custom D3 chart with the React-less template.

In the end, I had fun building this tech demo and ensure that Streamlit doesnā€™t slow you down on this. Iā€™d still keep D3 for ultra customized charts, there are many other great JS charting libraries from Recharts, Semiotic, react-vis to ZingChart, Chartist, HighCharts that you could try your hands on :slight_smile:

1 Like