Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working with D3.js and Immutable.js

In my application I use D3.js for some visualizations.

Now D3 works with mutable native JavaScript data structures.

So some data marshalling would be necessary for this to work with Immutable.js.

I also use Reflux with React, so in my Store I manage an Immutable Map. Since this Map becomes a new thing on every change I cannot just pass it to D3 Force Layout because that works with mutable data so each time it recalculates everything from scratch.

I end up with managing both immutable and mutable data structures but this feels very wrong.

I found an article, Practical Time Series Visualization using D3 + OM, which seems to touch on the subject and suggests to use Cursors. The problem is it uses Clojure(Script) when I'm using just JavaScript.

I understand this is very abstract without code examples but any suggestion on the subject of working/syncing both immutable and mutable data will be appreciated!

like image 457
sergelerner Avatar asked Apr 08 '15 12:04

sergelerner


2 Answers

I see no reason why d3.js should not work with immutable.js. In my opinion the key is to understand how d3.js deals with data, especially how d3.js' data joins work.

Data Joins - how data is bound to DOM elements in d3.js

Every time nodes are selected d3.selectAll('div') and joined with .data([1, 3, 5, 9]) d3.js compares if already existing div elements are bound to the data elements. This is done by evaluating if a selected div DOM node has a __data__ property associated. The data property is set and maintained by d3.js. By default the index within the joined array is what goes into __data__. But you can also define a key function to override this behavior.

Further reading

How selections work in which Mike Bostock explains how d3.js calculates the enter, update and exit selections through the data binding mechanism described above.

like image 61
rmoestl Avatar answered Nov 07 '22 08:11

rmoestl


In my opinion it is important to first understand the problem.

I assume you are dealing with some D3 functionality that mutates the data passed in, like d3-force and data is your applications state, like if you are using Immutable.js with React and Redux.

The problem is that D3 has no way to know how to handle Immutable.js data. Even if you find a way to get D3 to do that it won't be a good idea because it is not efficient in the case of d3-force when on every tick it will create a new immutable.

So what I suggest is to store your state in a plain JavaScript array or object for D3 to interact with.

For example, suppose you have a graph and when you add a node you dispatch some action(ADD_NODE) to your store and you might have a handler that updates your immutable state like reducers in Redux. What you want is to create another reducer that will take that node and return plain [...nodes]. The same for other CRUD actions. This way your data stays in sync. For example if a user clicks any of your nodes you just can get original data by index and all D3 mutations a saved.

Here is a example if you are using Redux. Hope it helps with gathering ideas.

const graphNodesReducer = (state = [], action) => {
     if (action.type in nodeTypes) {
         return [...state, action.payload];
         // or [...state, action.payload.toJS()] if node is sent as immutable
         // or [...state, action.payload.get('label')] you can choose what to set
     }
     return state;
}
like image 39
askbeka Avatar answered Nov 07 '22 08:11

askbeka