thomashoneyman/purescript-halogen-hooks

Q: In useState example `Hooks.get stateToken` is not really necessary right?

safareli opened this issue · 2 comments

In useState example Hooks.get stateToken is not really necessary right?

I think like that because if state changes this component will re-render and therefore that div will have new onClick listener referencing to new update which references new state and one can just reference state there instead of using Hooks.get stateToken. Is my reasoning right here? I.e. It's like that because it's sort of best practice right?

If yes, then adding something like this:

(In this particular example using state instead of Hooks.get stateToken would work correctly)

In Note: section might be useful

That's correct, it isn't always necessary to call Hooks.get to get the current state within effectful code. You only need to make sure to retrieve the latest value of state if it is possible that your effectful function will be called in a different context than the one it was defined in.

So as you explained, it's not necessary to call Hooks.get within update in this code, because the value it returns is going to be the same as state.

Hooks.do
 state /\ stateToken <- Hooks.useState initialState
 intState /\ intStateToken <- Hooks.useState 0

 let
   update :: HookM _ _ _ Unit
   update = do
     -- Get the current value of the first state
     currentState <- Hooks.get stateToken
     -- ...

 Hooks.useLifecycleEffect do
   Hooks.modify_ intStateToken (_ + 10)
   pure Nothing

 Hooks.pure $ HH.div
   [ HE.onClick \_ -> Just update ]
   [ HH.text $ show intState ]

But in the useLifecycleEffect block, for example, if I were to refer to the intState after the call to modify, or if I were to write some code in the finalizer that used a state value, then I would run into trouble. I'm not actually sure I need to emphasize this as much as I have; folks used to using Halogen already know that something like this won't work:

do
  state <- H.get
  H.put (state { field = 10 })

  -- `state` is not mutable, so this will overwrite the previous `put` statement
  H.put (state { otherField = "hello" })

because there's no mutable reference to state. This is heavily emphasized in React articles, but PureScript engineers wouldn't be expecting something mutable in the first place.

I've tried to just cover my tracks by preferring to use get when I am accessing state in effectful code, but perhaps this just leads to more confusion over when get would be necessary. I'm open to changing things at least with a note in that section.

Maybe the simplest solution for this particular case is to move the get call into the returned effect in useLifecycleEffect, where it really is necessary. This would help demonstrate what sort of case requires retrieving the current value.