Best way to design imperative operation in declarative way


#1

I have a API design question.

Assuming I have a some react/reagent components which does Physics simulation.
Like [Item {:density 10 :height 80 :width 50}]. Now if I need to enable some way to apply some force to this object, I can think of one way we can do declarative.
Providing a prop called ‘initial-force’. so [Item {:density 10 :initial-force [10,0]}], would apply force vector [10,0] after creation of the component.

How would I provide a way to apply force on already created components dynamically?

Currently in React, I create a reference to the instance of the component through refs. And then I call itemInstance.applyForce([10,0]) whenever I need. Its purely imperative. Is there any way I can express this declarative without too much magic?


#2

At first glance, I’d deal with all the forces outside of reagent, and just let the visualization be stateless.

Keep track of all your simulation nodes and forces and positions and masses etc somewhere in a big atom, update it every “tick”, let reagent re-render as needed.


#3

One possibility is to make each world object an atom and provide a few functions to operate on it. This is the approach taken in this example I wrote a few years ago using thi.ng. Source code here.


#4

I can’t find the tweet right now, but someone on the React core team said something along the lines of, “Treating props changes as an event is wrong.”

What you’re saying should probably belong in an event handler somewhere and then the actual position of the element is passed in as props.


#5

I think you are right. Thats the reason, this still stays as an imperative method for now.


#6

I am using external library Box2d for the physics simulation. It keep tracks the positions. For performance reasons, I just let the box2d handle those. In the example ph/add-force
is an imperative one (Just like I have one). For now, I have to conclude that applying force cannot be declarative. I didn’t know about thi.ng/geom until now. Thanks


#7

Well, you certainly can have a function like add-force that returns a new particle, and/or a function that loops over the world state and returns a new world state, but generally–as you have already surmised–this means lower performance.