I fetch some data once per second from the server. I know ideally WebSockets have to be used, but for starters I’m just sticking to a timer.
Now, I see the structure to the solution is like:
- Make
state /\ setState
pair viauseState
- Call to timer inside
useAff
, and use the pair from1
to change state as needed.
The problem though is that to make sure useAff
is called every time, it’d have to depend on state
. But if my timer wouldn’t change state just once (e.g. because data it fetched remained unchanged), it would never get called again!
Then, solution it seems is to ignore deps and to simply never return from the timer (by using forever
). But this doesn’t work because apparently React Hooks don’t re-render per setState
call.
Implementation for the latter:
module Main where
import Prelude
import Control.Monad.Rec.Class (forever)
import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff, Milliseconds(..), delay, forkAff)
import Effect.Class (liftEffect)
import Effect.Exception (throw)
import React.Basic.DOM as R
import React.Basic.DOM.Client (createRoot, renderRoot)
import React.Basic.Hooks (Component, component, useState')
import React.Basic.Hooks as React
import React.Basic.Hooks.Aff (useAff)
import Web.DOM.NonElementParentNode (getElementById)
import Web.HTML (window)
import Web.HTML.HTMLDocument (toNonElementParentNode)
import Web.HTML.Window (document)
main :: Effect Unit
main = do
doc <- document =<< window
root <- getElementById "root" $ toNonElementParentNode doc
case root of
Nothing -> throw "Could not find root."
Just container -> do
reactRoot <- createRoot container
app <- myComponent
renderRoot reactRoot (app {})
myTimer :: Int -> (Int -> Effect Unit) -> Aff Unit
myTimer count setCount = do
_ <- forkAff $ forever do
delay $ Milliseconds 1000.0
liftEffect $ setCount $ count + 1
pure unit
myComponent :: Component {}
myComponent =
component "Label" \_ -> React.do
count /\ setCount <- useState' 0
useAff unit $ myTimer count setCount
pure $ R.label {children: [R.text (show count)]}
So how to do that properly?