react-basic-hooks is a React hook API for react-basic.
Note: This library supports React >=16.8.0 with full React 19 support. For more info on hooks, see React's documentation.
I recommend using PureScript's "qualified do" syntax whilst using this library (it's used in the examples, the React.do bits).
It became available in the 0.12.2 compiler release.
This library provides the React.Basic.Hooks module, which can completely replace the React.Basic module.
It borrows a few types from the current React.Basic module like ReactComponent and JSX to make it easy to use both versions in the same project.
If we prefer this API over the existing react-basic API, we may eventually replace React.Basic with this implementation.
- React 16.8+: Core hooks (useState, useEffect, useReducer, useRef, useContext, useMemo, useDebugValue, useLayoutEffect)
- React 18+: useId, useTransition, useDeferredValue, useSyncExternalStore, useInsertionEffect
- React 19+: useOptimistic, useActionState, useEffectEvent (experimental)
mkCounter :: Component Int
mkCounter = do
component "Counter" \initialValue -> React.do
counter /\ setCounter <- useState initialValue
pure
$ R.button
{ onClick: handler_ do
setCounter (_ + 1)
, children:
[ R.text $ "Increment: " <> show counter ]
}Optimistically update the UI whilst waiting for an async action to complete. The optimistic state automatically reverts to the actual state when the action finishes.
mkMessageList :: Component Props
mkMessageList = do
component "MessageList" \{ messages } -> React.do
optimisticMessages /\ addOptimisticMessage <- useOptimistic messages \state newMessage ->
Array.snoc state newMessage
isPending /\ startTransition <- useTransition
let handleSend message = startTransition do
addOptimisticMessage message
-- Async operation to send message to server
sendToServer message
pure $ R.div_ (map renderMessage optimisticMessages)Manage form actions with built-in pending state. The action function receives the previous state and form data, making it ideal for form submissions. Uses Effect for synchronous operations.
mkForm :: Component Unit
mkForm = do
component "Form" \_ -> React.do
state /\ (formAction /\ isPending) <- useActionState initialState updateFn
where
updateFn prevState formData = do
-- Process form submission (Effect version)
result <- submitToServer formData
pure (Result.fromEither result)
pure $ R.button
{ disabled: isPending
, onClick: handler_ (formAction myFormData)
, children: [ R.text if isPending then "Submitting..." else "Submit" ]
}For progressive enhancement (form works without JavaScript), use useActionStateWithPermalink:
state /\ (formAction /\ isPending) <- useActionStateWithPermalink initialState updateFn "/api/submit"
pure $ R.form
{ action: formAction -- Falls back to /api/submit without JS
, children: [ ... ]
}Aff version of useActionState for async operations. Available in React.Basic.Hooks.Aff. Uses Aff for natural async handling.
import React.Basic.Hooks.Aff (useAffActionState)
mkForm :: Component Unit
mkForm = do
component "Form" \_ -> React.do
state /\ (formAction /\ isPending) <- useAffActionState initialState affFn
where
affFn prevState formData = do
-- Process form submission (Aff version - natural async!)
result <- Aff.submitToServer formData
pure (Result.fromEither result)
pure $ R.button
{ disabled: isPending
, onClick: handler_ (formAction myFormData)
, children: [ R.text if isPending then "Submitting..." else "Submit" ]
}With permalink: useAffActionStateWithPermalink initialState affFn "/api/submit"
Extract non-reactive logic from Effects. The returned function can access the latest props and state without causing the Effect to re-run when those values change.
mkComponent :: Component Props
mkComponent = do
component "Component" \{ url, onSuccess } -> React.do
count /\ setCount <- useState 0
-- onSuccess can use the latest count without re-running the effect
onSuccessEvent <- useEffectEvent \data -> do
onSuccess data count
-- Effect only re-runs when url changes, not when count changes
useEffect url do
response <- fetchData url
onSuccessEvent response
pure mempty
pure $ R.div_ [ ... ]useState/useState'— State managementuseEffect/useEffectOnce/useEffectAlways— Side effectsuseLayoutEffect— Synchronous layout effectsuseReducer— State management with reducersuseRef— Mutable refsuseContext— Context consumptionuseMemo— Memoised computationuseDebugValue— DevTools debugging labels
useId— Unique ID generationuseTransition— Concurrent transitionsuseDeferredValue— Deferred value updatesuseSyncExternalStore— External store synchronisationuseInsertionEffect— DOM mutation effects
useOptimistic— Optimistic UI updatesuseActionState— Form action managementuseEffectEvent— Non-reactive effect logic (experimental)
memo/memo'— Component memoisationcomponent— Component creation- Custom hooks via
React.Basic.Hooks.Afffor async effects React.Basic.Hooks.Suspensefor Suspense supportReact.Basic.Hooks.ErrorBoundaryfor error boundaries