React & D3: Rendering Circles On A Map Using React-Spring Transitions
By: Joe Keohan
Tags: Data Visualization, D3js, React, React Spring

Image by author

In my last article React & D3: Rendering A Map I walked through the code to render a map using both the D3 within React and D3(math)/React(DOM) approaches. It’s a great entry point for anyone with D3 expertise but relatively new to the React.

In this article I will perform one last refactor to render the circles using the D3(math)/React(DOM) approach. This means I’ll be migrating as much of the code from D3 over to React, which in this case is almost all of it.

Existing Circle Codebase (D3v4)

Here is how I originally wrote the existing D3 specific code. It follows the older D3 (pre V5) Enter/Update/Exit pattern and includes the transitions to fade in/out the circles.

Image by author

Small Refactor Of Existing Codebase from D3v4 To D3v5

Since I’ve been so focused on working with React these days I really haven’t kept up with any of the new additions to D3v5/v6. So i thought to myself maybe this was a good opportunity to refactor the code slightly to incorporate the d3.join() method that was introduced in v5 and replace the older Enter/Update/Exit pattern.

For the record I’ve never worked with d3.join() before and was able to implement the code fairly easy, with one small bump which I’ll speak to shortly.

Image by author

This approach requires a bit more code including 3 separate callbacks for each of the Enter/Update/Exit phases. It requires additional callbacks via .call() to execute the transitions. I’m not completely sold on this approach and have a hard time seeing the benefits of the additional code.

I also encountered one small issue where transitions didn’t occur if I didn’t include the update callback function. Since I’m not updating any of the circles, and didn’t include any updates in the previous code, I was surprised I needed it. It seems I’ll have to delve a bit further at some point to understand what’s truly going on here.

The Circle Components

After refactoring the map, it became quite clear that the circles could easily be spun off into their own components. I originally thought about including that code inside of the Map component, but the circles might require their useState or useEffect in order to manage the transitions.

Inside of the Map folder I created two new folders. One was responsible for rendering all the circles, and the other the actual Circle component.

Image by author

The Circles Component

Let’s start with the Circles component. This really was just a function that would iterate over the array of data and render individual Circle components.

I took this approach as its a standard way of segmenting code used for rendering structured elements like ul > li’s as in a TodoList and TodoListItem.

Image by author

The component required the following props:

allParks / activeParks arrays

map projection

It loops over the allParks array and then sets the isShowing prop to true/false depending if the park is included in the activeParks array. That value will change depending on any user interaction that filters the data causing a re-render.

Iterating over the array and using the key property to allow React to manage those elements, essentially replaces D3’s Data Binding & Enter/Exit methods and the isShowing prop replaces the Update functionality.

I feel as though there may be another solution that involves using the Circle component’s componentWillUnmount lifecycle method to control the transition just before it’s removed from the DOM but this worked for now so I ran with it.

The Circle Component

So this one took the most time to implement. I needed to replace the d3.transition method with another implementation of CSS transitions. After some research I decided to go with React-Spring. The two main features of React-Spring that I used were the useSpring hook and animated.

Image by author

Let’s break down the React-Spring code a bit further. The useSpring hook takes in an object that includes a config for any configurations. In this case I only needed to include the duration but there are other config options available.

The other key I added was to target the specific CSS property to transition, in this case opacity. Also being that its JS I was able to use a ternary operator to conditionally render that value.

Image by author

The next step was to prepend the circle element with animated. This immediately reminded me Styled Components in that they are written in a similar syntax where the DOM element is preceded by styled as in: styled.circle``

Image by author

Now it was just a matter of passing down the necessary props to Circles > Circle component and the refactor would be complete.

The Map Component

Once the Circles component was imported into Map it required the same conditional logic to render only when the data was present.

Image by author

Conclusion

I have to say that I prefer this approach over using D3 to render the circles. I suppose it’s because I’ve been working so heavily with React these days that I wholeheartedly believe it should manage the DOM whenever possible, which is almost always.

I also discovered React-Spring transitions in the process, and I’m super excited to incorporate the library into future projects.

Here is the CodeSandbox solution that includes both refactors. All you need to do is go into the App component and adjust the comments to display the one you want to test.

As always, I’d love to hear any feedback/suggestions regarding the code so feel free to reach out with any thoughts.