Skip to content

working-group-purescript-es/purescript-elmish-hooks

 
 

Repository files navigation

purescript-elmish-hooks

This library offers an analog of React Hooks for use with PureScript Elmish.

Getting Started

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

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 Hooks.do
  todos /\ setTodos <- useState []

  useEffect do
    todos <- API.fetchTodos
    liftEffect $ setTodos todos

  Hooks.pure $
    H.fragment $ todoView <$> todos

Custom Hooks

Custom hooks can also be created. One way is to build on other hooks using the Hook monad:

type UseLocalStorage t = UseState String <> UseEffect Unit <> t

useLocalStorage :: String -> String -> Hook UseLocalStorage (String /\ Dispatch String)
useLocalStorage key defaultValue = Hooks.do
  state /\ setState <- useState defaultValue

  useEffect $ liftEffect do
    window >>= localStorage >>= getItem key >>= case _ of
      Just v -> setState v
      Nothing -> setItem key defaultValue =<< localStorage =<< window

  Hooks.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:

foreign import data :: UseMousePosition :: HookType

useMousePosition :: String -> Hook UseMousePosition (Maybe { x :: Number, y :: Number })
useMousePosition className =
  mkHook (ComponentName "UseMousePosition") \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
    }

Continuation-Passing Style

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 }

Examples

There are some examples in the examples folder, which can be seen live here.

Documentation

Documentation is published on Pursuit.

About

React hooks for Elmish

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • PureScript 91.1%
  • JavaScript 5.3%
  • Dhall 3.6%