reagent 2026-09-02

is it better to pass an ratom to child components and let them create cursors as necessary, or to create the cursors in the entrypoint component and pass only those cursors to each child component? and if i should be creating cursors, how many is too many?

Cursors are lightweight objects and it is fine to create them when you need to. The question is more about data scoping: what information you want to give to your children, do they need the full state or not. Sometimes by creating the cursor in a parent, you make your children more flexible about the data they can receive and so less dependant on the global state. But I'd say optimization is really not the main question here, until there's a real performance issue. I can't answer for how many is too many it's quite well optimized but depends of the structure of the state and of your components. But note that it will be faster to have a cursor value check than rendering the component that is watching the cursor, so it's usually a good idea to use them a lot.

👍 1

this is for a multiplayer card game with fairly complex game state, so we're building something like 60 cursors to look at various aspects of the state and then passing those as necessary to the child components

60 is totally fine

Also if you have a complex state and want to pre-optimize a bit you could chain your cursors instead of defining all of them from the ratom.

(let [board-c (r/cursor gstate [:board])
      first-slot-c (r/cursor board-c [:slots 0])]
With cursors on the ratoms there will be 60 checks everytime the ratom changes (still fine tho), but by cascading you are not checking the untouched branches of the state.

😮 1

woah! i didn't realize you could chain cursors, that's really sick

that will be really helpful, thank you

😉 1

I've just remembered an issue relating to that tho. If you update the ratom (directly or with a cursor, doesn't matter), the deref of the level 2+ cursors will give the cached value until the next render (or a manual call to r/flush). In my example:

@first-slot-c ; => 41
(swap! first-slot-c inc) ; => 42 (the right value is returned)
@first-slot-c ; => 41, still the same as before for now

(r/flush) ; or rerender of the components that is usually done the frame after the swap! is called
@first-slot-c ; => 42, now is the updated value
It acts a bit like the state/setState in React, setState does not update the state variable directly. In Reagent with an explicit deref this behaviour is not obvious. Note that it works well with level 1 cursor (depending directly on the ratom), they don't return a cached value in that case.

👀 1