This library offers an analog of React Hooks for use with PureScript Elmish.
To use this library, install elmish-hooks
, as well as the npm package stacktrace-parser
.
npx spago install elmish-hooks
npm install stacktrace-parser --save
Hooks allow introducing local state or effects without writing a new component. This library comes with two builtin hooks: useState
and useEffect
:
todos :: ReactElement
todos = withHooks do
todos /\ setTodos <- useState []
useEffect do
todos <- API.fetchTodos
liftEffect $ setTodos todos
pure $
H.fragment $ todoView <$> todos
NOTE: Hooks should always be used either:
- At the top level of a
withHooks
block, not inside any conditionals- At the top level of a “custom hook”
Custom hooks can also be created. One way is to build on other hooks using the Hook
monad:
useLocalStorage :: String -> String -> Hook (String /\ Dispatch String)
useLocalStorage key defaultValue = do
state /\ setState <- useState defaultValue
useEffect $ liftEffect do
window >>= localStorage >>= getItem key >>= case _ of
Just v -> setState v
Nothing -> setItem key defaultValue =<< localStorage =<< window
pure $ state /\ \v -> do
setState v
setItem key v =<< localStorage =<< window
A more flexible approach, when that doesn’t work, is to use the mkHook
function provided by this library:
useMousePosition :: String -> Hook (Maybe { x :: Number, y :: Number })
useMousePosition className =
mkHook name \render ->
{ init: pure Nothing
, update: \_ pos -> pure pos
, view: \pos dispatch ->
H.div_ className
{ onMouseMove: unsafeCoerce $ mkEffectFn1 \(event :: { clientX :: Number, clientY :: Number, currentTarget :: HTMLElement }) -> do
{ top, left, width, height } <- getBoundingClientRect event.currentTarget
let
x = event.clientX - left
y = event.clientY - top
mouseLeft = x < 0.0 || y < 0.0 || y > height || x > width
dispatch if mouseLeft then Nothing else Just { x, y }
, onMouseLeave: dispatch <| const Nothing
} $
render pos
}
where
name = uniqueNameFromCurrentCallStack { skipFrames: 3, prefix: "UseMousePosition" }
If you're only using a single hook, sometimes it might be more concise to use CPS via the ==>
or =/>
operators.
myInput :: ReactElement
myInput = useState "" =/> \name setName ->
H.input_ "" { value: name, onChange: setName <?| eventTargetValue }
There are some examples in the examples folder, which can be seen live here.
Documentation is published on Pursuit.