I’ve read @captainn’s posts about the awesome new useTracker and have used it in other projects with no problems whatsoever. But here, I feel like I’m missing something. Can anybody help me sort this out?
The D3 method chain is trying to manually update the DOM, and React loses track of its internal representation. Look into portals if you want low-level granular control. Otherwise, try using a library like Nivo, which has already put common D3 graphs into reusable React components using portals. useTracker will work as expected with the Nivo libraries.
My guess is you have something of a race condition, and a useEffect deps config issue here.
You don’t want the useEffect hook to run until the animatables array is loaded. But you don’t have any loading logic here. You probably want to check the subscription’s ready() method and send the result of that to the useEffect hook, and do nothing until it’s loaded.
Additionally, I don’t think the animatables prop will trigger a rerun of the useEffect hook anyway, because I doubt that’s immutable (I actually don’t recall if it gets you a new array ref or not). You’ll probably want to pass in the specific props you are accessing inside the useEffect hook instead of the containing object ref.
@awatson1978, I’m not sure of the strategy I should follow to solve the problem using portals. I didn’t know about Nivo and although it looks very nice and curated, I need pure D3 on this.
It works the same with the loading logic but it’s a neat detail
I think you struck on the core problem here:
It does work for entering and exiting elements but not for properties changes so I’m guessing useEffect is overlooking the actual radius change.
And this made me realize that my actual code is not exactly like the one I posted and that is probably the actual key of it all. Ok, let me explain. When I wrote
enter => enter
.append("circle")
.attr("r", a => a.radius)
It all comes down to the point that the property “radius” is in fact an array of objects that actually determines the final value of the radius for each circle. I don’t think useEffect is deep-comparing the values of that array or the values contained in those objects… this could be it, right?
useEffect does definitely not do a deep compare - it’s shallow. You can overcome this by passing the object properties you want to access on objects instead of the object reference itself. For arrays it’s trickier, and you’ll either want to control immutability yourself (easy way - always clone the array after .fetch() and return that clone in useTracker) or trigger the rerun of useEffect off another value, like the loading flag.