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 ComponentDef
. This library comes with three builtin hooks: useState
, useEffect
, and useRef
. Here’s what hooks look like in practice:
todos :: ReactElement
todos = Hooks.component Hooks.do
todos /\ setTodos <- useState []
useEffect do
todos <- API.fetchTodos
liftEffect $ setTodos todos
Hooks.pure $
H.fragment $ todoView <$> todos
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: E.handleEffect \(E.MouseEvent event) -> 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
}
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 <| E.inputText }
There are some examples in the examples folder, which can be seen live here.
Documentation is published on Pursuit.