3
votes

I am trying to use purescript-thermite to build an application using websockets. The idea is that the application connects to some server using websockets and live-updates the HTML page. However, I cannot find a way how to wire it into the thermite workflow.

I have a spec that is made up of render and performAction. The render has access to the dispatch function. However, I need to start the websockets before rendering the element (I could probably put it e.g. into main), but upon arrival of a message I need to ideally dispatch an event to the component from the outside. What is the best way to do that?

2

2 Answers

5
votes

The expectation is that you would render your component, getting a handle to the driver function, then set up your websocket connection and use the driver function to provide updates.

However, if you need the websocket connection to be set up first for some reason, then you will need to employ some trickery, probably involving a Ref to hold the driver function once setup is complete. This way, you'd need to make manually verify that you don't try to call the driver function before the Ref is updated.

A more advanced solution could be to wrap your websocket protocol in the form of a coroutine (see purescript-coroutines), and to represent any setup phase explicitly in the types:

type Connection = 
  Aff MyEffects { initialData :: InitialData
                , updateStream :: Producer (Aff MyEffects) UpdateMessage
                }

Here, InitialData represents the data you receive at setup time, which might be passed to the component, and UpdateMessage represents your incremental updates from the server, which would be passed to the driver function. You could then wire all of this up in main.

4
votes

I am not sure if it is not the 'right' way, but it works. In order to access the websocket connection I had to put it into the State. To initialize it I put it into the componentWillMount function. So the initialization looks like this:

type State = {
    conn :: Maybe Connection
}

main :: Eff (...) Unit
main = do
  let rspec = T.createReactSpec spec initialState
  let component = React.createClass $ rspec.spec {componentWillMount=mountInit rspec.dispatcher}

mountInit :: forall props eff.
  (ReactThis props State -> Action ->
      Eff (... | eff) Unit)
  -> ReactThis props State
  -> Eff (... | eff) Unit
mountInit dispatch this = do
  let handlers = {
        connected: log "Service connected"
      , disconnected: log "Disconnected"
      , handle: \msg -> dispatch this (WebsockMsg msg)
    }
  conn <- createConnection "ws://localhost:3000/websock/webclient" handlers
  void $ transformState this (\s -> s{conn= Just conn})