purescript-thermite icon indicating copy to clipboard operation
purescript-thermite copied to clipboard

Nesting Specs

Open spencerjanssen opened this issue 9 years ago • 8 comments

Thermite has the concept of sequencing Specs via the Semigroup instance, but sometimes you want to nest the markup of a Spec inside another. Lately I've been using this helper function:

wrap :: forall eff state props action.
    Spec eff state props action ->
    Spec eff state props action ->
    Spec eff state props action
wrap parent child = simpleSpec pa rn 
 where
    pa a p st k = do
        view _performAction parent a p st k
        view _performAction child a p st k
    rn k p st children = view _render parent k p st (view _render child k p st children)

At this point, I'm not sure whether this re-appropriation of the children argument is elegant or hacky. Does it make sense to add a function like this to Thermite? I'm definitely open to better names as well.

spencerjanssen avatar Jan 29 '16 00:01 spencerjanssen

Hmm, that's interesting. I expected users would pass a Spec for any inner component as a function argument, but I suppose it makes sense to use the children property instead. I suppose it is a little hacky, but then that argument has always been undocumented, so we can call this the default going forward, and document that any React-provided children will be passed in at the top level.

I think the name wrap might be used though.

paf31 avatar Jan 29 '16 01:01 paf31

Another question is: should the children be passed as a singleton array? Or even pass an array of child Specs?

paf31 avatar Jan 29 '16 01:01 paf31

This is what I came up with:

render dispatch _ state _ = 
   [ R.div [...] [ view T._render childSpec dispatch {} state [] ]

childSpec can of course be focused to use some substate/action. Is there a more idiomatic way to do that?

Furthermore, what would props be used for in Thermite? When using focused specs there's only one global state anyway, or am I missing something?

pkamenarsky avatar Feb 03 '16 14:02 pkamenarsky

I'm not sure how useful that would be, but if Render was a Reader monad, it would be possible to pass down dispatch/state to child specs implicitly.

pkamenarsky avatar Feb 03 '16 14:02 pkamenarsky

@pkamenarsky I think focusing would use a separate call to focusState etc.

Props are used at the top-level when the full component is integrated into a larger React application.

I'm interested in the Reader approach, for sure, but it adds a cognitive overhead for new users which I'd like to avoid. Right now, all you need to get started is functions and data types (I consider the lens stuff to be an advanced feature, but even then, you don't need to know about type classes or monads to use it).

paf31 avatar Feb 03 '16 17:02 paf31

@paf31 Yes, focusing is a separate call (i.e. childSpec = focus ...). What I meant though is that it would be cool to be able to nest components the same way other elements are nested:

[ R.div [...] [ chidComponent [...] [...] ]

without having to call render explicitly. But I guess that would only be possible with the Reader approach - and I agree, the mental overhead probably doesn't justify that.

re props - so if the whole application is written in Thermite, props are basically useless?

pkamenarsky avatar Feb 03 '16 17:02 pkamenarsky

Pretty much, on the props thing.

paf31 avatar Feb 03 '16 17:02 paf31

I have an almost identical function

nestSpec :: forall eff state props action. T.Spec eff state props action -> T.Spec eff state props action -> T.Spec eff state props action
nestSpec parent child = T.simpleSpec performAction render
 where
   performAction a p st = do
     view T._performAction parent a p st
     view T._performAction child a p st
   render k p st children = view T._render parent k p st (view T._render child k p st children)

Would you be open for a PR?

teh avatar Sep 20 '16 12:09 teh