diff --git a/README.md b/README.md index 77c14bb..ad16e50 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # react-basic-hooks [![CircleCI](https://circleci.com/gh/spicydonuts/purescript-react-basic-hooks.svg?style=svg)](https://circleci.com/gh/spicydonuts/purescript-react-basic-hooks) -This is library adds React hooks to [react-basic](https://github.com/lumihq/purescript-react-basic). +`react-basic-hooks` adds React hook support to [react-basic](https://github.com/lumihq/purescript-react-basic)! -_Warning:_ This API relies on recent React versions (>= 16.8.0). +_Note:_ This API relies on recent React versions (>= 16.8.0). For more info on hooks, see [React's documentation](https://reactjs.org/docs/hooks-intro.html). -I recommend using PureScript's new "qualified do" syntax while using this library (it's used in the examples, the `React.do` bits). +I recommend using PureScript's "qualified do" syntax while 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. @@ -20,10 +20,13 @@ mkCounter = do component "Counter" \props -> React.do counter /\ setCounter <- useState 0 - React.pure $ R.button - { onClick: handler_ $ setCounter (_ + 1) - , children: [ R.text $ "Increment: " <> show counter ] - } + pure + $ R.button + { onClick: handler_ do + setCounter (_ + 1) + , children: + [ R.text $ "Increment: " <> show counter ] + } ``` More examples: @@ -36,7 +39,3 @@ More examples: - [A Todo App](./examples/todo-app/src/TodoApp.purs) (components, inputs, state) - [Context](./examples/context/src/Context.purs) (creating and consuming React context) - [Aff helper](./examples/aff/src/AffEx.purs) (async state management) - -_A note on Refs:_ The `Ref` type is useful for all kinds of state (anything which shouldn't trigger a render when changed), particularly references to DOM nodes as in the example. -Unfortunately, while this module remains a small extension to the existing react-basic library it won't be possible to pass a `ref` prop to the native DOM components from `React.Basic.DOM`. -In the meantime, use `element (unsafeCreateDOMComponent "div") { ref: elementRef }`. diff --git a/bower.json b/bower.json index 5f37f5f..dfe5ef2 100644 --- a/bower.json +++ b/bower.json @@ -15,7 +15,7 @@ "purescript-prelude": "^4.1.0", "purescript-console": "^4.2.0", "purescript-effect": "^2.0.0", - "purescript-react-basic": "^8.0.0 || ^9.0.0", + "purescript-react-basic": "^10.0.0", "purescript-indexed-monad": "^1.0.0", "purescript-unsafe-reference": "^3.0.1", "purescript-aff": "^5.1.1" diff --git a/examples/aff/src/AffEx.purs b/examples/aff/src/AffEx.purs index f9d56b4..445ad28 100644 --- a/examples/aff/src/AffEx.purs +++ b/examples/aff/src/AffEx.purs @@ -1,7 +1,6 @@ module AffEx where import Prelude - import Data.Either (either) import Data.Maybe (Maybe(..), maybe) import Effect.Aff (Aff, Milliseconds(..), delay, error, message, throwError) @@ -15,19 +14,18 @@ mkAffEx :: CreateComponent {} mkAffEx = do -- A component for fetching and rendering a Cat entity. catDetails <- mkCatDetails - component "AffEx" \props -> React.do catKey /\ catChooser <- useCatKeyChooser - - pure $ R.div - { style: R.css { display: "flex", flexFlow: "column" } - , children: + pure + $ R.div + { style: R.css { display: "flex", flexFlow: "column" } + , children: [ R.h2_ [ R.text "Cat chooser" ] , R.p_ - [ R.text $ - "Select a key to fetch! If you get bored (how would you even!?) " <> - "try holding your arrow keys to select really fast! The result " <> - "always matches the chosen key." + [ R.text + $ "Select a key to fetch! If you get bored (how would you even!?) " + <> "try holding your arrow keys to select really fast! The result " + <> "always matches the chosen key." ] , catChooser , R.p_ @@ -36,60 +34,63 @@ mkAffEx = do Just k -> element catDetails { catKey: k } ] ] - } + } where - -- This hook packages up some interactive UI and the current - -- selection the user has made via that UI. - useCatKeyChooser :: Hook _ ((Maybe (Key Cat)) /\ JSX) - useCatKeyChooser = React.do - catKey /\ setCatKey <- useState Nothing - let - catChoice key = - R.label_ - [ R.input - { type: "radio" - , name: "cat-key" - , checked: Just key == catKey - , onChange: handler_ do - setCatKey \_ -> Just key - } - , R.text $ showCatKey key - ] - - showCatKey :: Key Cat -> String - showCatKey (Key key) = "Cat " <> key + -- This hook packages up some interactive UI and the current + -- selection the user has made via that UI. + useCatKeyChooser :: Hook _ ((Maybe (Key Cat)) /\ JSX) + useCatKeyChooser = React.do + catKey /\ setCatKey <- useState Nothing + let + catChoice key = + R.label_ + [ R.input + { type: "radio" + , name: "cat-key" + , checked: Just key == catKey + , onChange: + handler_ do + setCatKey \_ -> Just key + } + , R.text $ showCatKey key + ] - pure $ catKey /\ fragment - [ catChoice $ Key "abc" - , catChoice $ Key "def" - , catChoice $ Key "xyz" - ] - - -- Hooks can't be used conditionally but components can! - -- Not needing to deal with a `Maybe` key simplifies this - -- compoennt a bit. - mkCatDetails :: CreateComponent { catKey :: Key Cat } - mkCatDetails = do - component "CatDetails" \{ catKey } -> React.do - cat <- useAff catKey $ fetch catKey - pure $ R.text $ - maybe "Loading..." (either message showCat) cat - where - showCat (Cat { name }) = "A cat named " <> name + showCatKey :: Key Cat -> String + showCatKey (Key key) = "Cat " <> key + pure $ catKey + /\ fragment + [ catChoice $ Key "abc" + , catChoice $ Key "def" + , catChoice $ Key "ghi" + , catChoice $ Key "xyz" + ] + -- Hooks can't be used conditionally but components can! + -- Not needing to deal with a `Maybe` key simplifies this + -- compoennt a bit. + mkCatDetails :: CreateComponent { catKey :: Key Cat } + mkCatDetails = do + component "CatDetails" \{ catKey } -> React.do + cat <- useAff catKey $ fetch catKey + pure $ R.text + $ maybe "Loading..." (either message showCat) cat + where + showCat (Cat { name }) = "A cat named " <> name -- Typed keys are a great way to tie entity-specific behavior -- to an ID. We can use this phantom type to write a class -- for generic, type-safe data fetching. -newtype Key entity = Key String +newtype Key entity + = Key String + derive instance eqKey :: Eq (Key entity) class Fetch entity where fetch :: Key entity -> Aff entity - -- An example entity -newtype Cat = Cat { name :: String } +newtype Cat + = Cat { name :: String } instance fetchCat :: Fetch Cat where fetch = case _ of @@ -99,6 +100,9 @@ instance fetchCat :: Fetch Cat where Key "def" -> do delay $ Milliseconds 600.0 pure $ Cat { name: "Maxi" } + Key "ghi" -> do + delay $ Milliseconds 900.0 + pure $ Cat { name: "Chloe" } _ -> do delay $ Milliseconds 900.0 throwError $ error "Cat not found (intended example behavior 😅)" diff --git a/examples/context/src/Context.purs b/examples/context/src/Context.purs index 3b073b7..954afc5 100644 --- a/examples/context/src/Context.purs +++ b/examples/context/src/Context.purs @@ -1,56 +1,48 @@ module Context where import Prelude - -import Data.Maybe (Maybe(..)) import Effect (Effect) import React.Basic.DOM as R import React.Basic.Events (handler_) -import React.Basic.Hooks (Context, CreateComponent, JSX, type (/\), component, contextProvider, createContext, element, fragment, useContext, useState, (/\)) +import React.Basic.Hooks (type (/\), CreateComponent, JSX, ReactContext, component, createContext, element, provider, useContext, useState, (/\)) import React.Basic.Hooks as React mkContext :: CreateComponent {} mkContext = do - - counterContext <- createContext + counterContext <- createContext (0 /\ pure unit) store <- mkStore counterContext counter <- mkCounter counterContext - component "Context" \props -> React.do - - pure $ element store - { children: - [ element counter {} - , element counter {} - , element counter {} - ] - } - -mkStore - :: Context (Int /\ (Effect Unit)) - -> CreateComponent { children :: Array JSX } + pure + $ element store + { children: + [ element counter {} + , element counter {} + , element counter {} + ] + } + +mkStore :: + ReactContext (Int /\ (Effect Unit)) -> + CreateComponent { children :: Array JSX } mkStore context = do component "Store" \{ children } -> React.do counter /\ setCounter <- useState 0 - let increment = setCounter (_ + 1) - pure $ - contextProvider context - (counter /\ increment) - (fragment children) - -mkCounter - :: Context (Int /\ (Effect Unit)) - -> CreateComponent {} + let + increment = setCounter (_ + 1) + pure + $ provider context + (counter /\ increment) + children + +mkCounter :: + ReactContext (Int /\ (Effect Unit)) -> + CreateComponent {} mkCounter counterContext = do component "Counter" \props -> React.do - mCounter <- useContext counterContext - - case mCounter of - Nothing -> do - pure $ R.text "no counter value found" - Just (counter /\ increment) -> do - pure $ - R.button - { onClick: handler_ increment - , children: [ R.text $ "Increment: " <> show counter ] - } + counter /\ increment <- useContext counterContext + pure + $ R.button + { onClick: handler_ increment + , children: [ R.text $ "Increment: " <> show counter ] + } diff --git a/examples/todo-app/src/TodoApp.purs b/examples/todo-app/src/TodoApp.purs index 07e869e..cff333b 100644 --- a/examples/todo-app/src/TodoApp.purs +++ b/examples/todo-app/src/TodoApp.purs @@ -1,7 +1,6 @@ module TodoApp where import Prelude - import Data.Array as Array import Data.Foldable (traverse_) import Data.Maybe (Maybe(..)) @@ -24,140 +23,143 @@ data TodoFilter = All | Complete | Incomplete + derive instance eqTodoFilter :: Eq TodoFilter -type Todo = - { task :: String - , isComplete :: Boolean - } +type Todo + = { task :: String + , isComplete :: Boolean + } -type State = - { todos :: Array Todo - , filter :: TodoFilter - } +type State + = { todos :: Array Todo + , filter :: TodoFilter + } reducer :: State -> Action -> State reducer state = case _ of - CreateTodo task -> - state { todos = Array.cons { task, isComplete: false } state.todos } - - ToggleTodo index -> - case Array.modifyAt index (\todo -> todo { isComplete = not todo.isComplete }) state.todos of - Just todos -> - state { todos = todos } - Nothing -> - state - - DeleteTodo index -> - case Array.deleteAt index state.todos of - Just todos -> - state { todos = todos } - Nothing -> - state - - SetFilter filter -> - state { filter = filter } + CreateTodo task -> state { todos = Array.cons { task, isComplete: false } state.todos } + ToggleTodo index -> case Array.modifyAt index (\todo -> todo { isComplete = not todo.isComplete }) state.todos of + Just todos -> state { todos = todos } + Nothing -> state + DeleteTodo index -> case Array.deleteAt index state.todos of + Just todos -> state { todos = todos } + Nothing -> state + SetFilter filter -> state { filter = filter } mkTodoApp :: CreateComponent {} mkTodoApp = do - let initialState = { todos: [], filter: All } + let + initialState = { todos: [], filter: All } todoInput <- memo mkTodoInput todoRow <- memo mkTodoRow todoFilters <- memo mkTodoFilters - component "TodoApp" \props -> React.do - state /\ dispatch <- useReducer initialState reducer - pure $ R.div - { children: + state /\ dispatch <- useReducer initialState reducer + pure + $ R.div + { children: [ element todoInput { dispatch } - , R.div_ $ flip Array.mapWithIndex state.todos \id todo -> - if state.filter == All || - (todo.isComplete && state.filter == Complete) || - (not todo.isComplete && state.filter == Incomplete) - then elementKeyed todoRow { key: show id, id, todo, dispatch } - else empty + , R.div_ + $ flip Array.mapWithIndex state.todos \id todo -> + if state.filter == All + || (todo.isComplete && state.filter == Complete) + || (not todo.isComplete && state.filter == Incomplete) then + elementKeyed todoRow { key: show id, id, todo, dispatch } + else + empty , element todoFilters { filter: state.filter, dispatch } ] - , style: R.css - { maxWidth: "600px" - , margin: "auto" - , padding: "16px" - , fontFamily: "sans-serif" - , fontSize: "16px" - } - } - + , style: + R.css + { maxWidth: "600px" + , margin: "auto" + , padding: "16px" + , fontFamily: "sans-serif" + , fontSize: "16px" + } + } where - todoAppEl = RB.element $ R.unsafeCreateDOMComponent "todo-app" + todoAppEl = RB.element $ R.unsafeCreateDOMComponent "todo-app" mkTodoInput :: CreateComponent { dispatch :: Action -> Effect Unit } mkTodoInput = do component "TodoInput" \props -> React.do value /\ setValue <- useState "" - pure $ R.form - { onSubmit: handler (preventDefault >>> stopPropagation) \_ -> do - props.dispatch $ CreateTodo value - setValue $ const "" - , children: - [ R.input - { value - , onChange: - handler (preventDefault >>> stopPropagation >>> targetValue) $ - traverse_ (setValue <<< const) - , style: R.css { lineHeight: "32px", width: "100%", boxSizing: "border-box" } - , placeholder: "Enter a task" - } - ] - , style: R.css { marginBottom: "16px", width: "100%" } - } + pure + $ R.form + { onSubmit: + handler (preventDefault >>> stopPropagation) \_ -> do + props.dispatch $ CreateTodo value + setValue $ const "" + , children: + [ R.input + { value + , onChange: + handler (preventDefault >>> stopPropagation >>> targetValue) + $ traverse_ (setValue <<< const) + , style: R.css { lineHeight: "32px", width: "100%", boxSizing: "border-box" } + , placeholder: "Enter a task" + } + ] + , style: R.css { marginBottom: "16px", width: "100%" } + } mkTodoRow :: CreateComponent { id :: Int, todo :: Todo, dispatch :: Action -> Effect Unit } -mkTodoRow = component "Todo" \props -> React.do - pure $ R.div - { children: - [ R.label - { children: - [ R.input - { type: "checkbox" - , checked: props.todo.isComplete - , onChange: Events.handler_ $ props.dispatch $ ToggleTodo props.id - , tabIndex: "0" - } - , R.text props.todo.task - ] - , style: R.css { lineHeight: "32px", fontSize: "24px", flex: "1 0 auto" } - } - , R.a - { children: [ R.text "❌" ] - , onClick: handler_ $ props.dispatch $ DeleteTodo props.id - , style: R.css { cursor: "pointer" } - } - ] - , style: R.css - { display: "flex" - , flexFlow: "row" - , alignItems: "center" - } - } +mkTodoRow = + component "Todo" \props -> React.do + pure + $ R.div + { children: + [ R.label + { children: + [ R.input + { type: "checkbox" + , checked: props.todo.isComplete + , onChange: Events.handler_ $ props.dispatch $ ToggleTodo props.id + , tabIndex: 0 + } + , R.text props.todo.task + ] + , style: R.css { lineHeight: "32px", fontSize: "24px", flex: "1 0 auto" } + } + , R.a + { children: [ R.text "❌" ] + , onClick: handler_ $ props.dispatch $ DeleteTodo props.id + , style: R.css { cursor: "pointer" } + } + ] + , style: + R.css + { display: "flex" + , flexFlow: "row" + , alignItems: "center" + } + } mkTodoFilters :: CreateComponent { filter :: TodoFilter, dispatch :: Action -> Effect Unit } -mkTodoFilters = component "TodoFilters" \props -> React.do - let - filterLink f label = R.a - { children: [ R.text label ] - , onClick: handler_ $ props.dispatch $ SetFilter f - , style: if props.filter == f - then R.css { cursor: "pointer", fontWeight: "bold" } - else R.css { cursor: "pointer" } - } - pure $ R.div - { children: - [ R.hr { style: R.css { color: "lightgrey" } } - , filterLink All "All" - , R.text " / " - , filterLink Complete "Complete" - , R.text " / " - , filterLink Incomplete "Incomplete" - ] - , style: R.css { marginTop: "16px" } - } +mkTodoFilters = + component "TodoFilters" \props -> React.do + let + filterLink f label = + R.a + { children: [ R.text label ] + , onClick: handler_ $ props.dispatch $ SetFilter f + , style: + if props.filter == f then + R.css { cursor: "pointer", fontWeight: "bold" } + else + R.css { cursor: "pointer" } + } + pure + $ R.div + { children: + [ R.hr { style: R.css { color: "lightgrey" } } + , filterLink All "All" + , R.text " / " + , filterLink Complete "Complete" + , R.text " / " + , filterLink Incomplete "Incomplete" + ] + , style: R.css { marginTop: "16px" } + } diff --git a/src/React/Basic/Hooks.js b/src/React/Basic/Hooks.js index 734f149..0826850 100644 --- a/src/React/Basic/Hooks.js +++ b/src/React/Basic/Hooks.js @@ -54,12 +54,6 @@ exports.writeRef_ = function(ref, a) { exports.useContext_ = React.useContext; -exports.createContext = React.createContext; - -exports.contextProvider_ = function(context) { - return context.Provider; -}; - exports.useMemo_ = function(eq, key, computeA) { var memoizedKey = exports.useEqCache_(eq, key); return React.useMemo(computeA, [memoizedKey]); diff --git a/src/React/Basic/Hooks.purs b/src/React/Basic/Hooks.purs index eba4b0a..3621f3a 100644 --- a/src/React/Basic/Hooks.purs +++ b/src/React/Basic/Hooks.purs @@ -24,7 +24,6 @@ module React.Basic.Hooks , UseReducer , useReducer , UseRef - , Ref , readRef , readRefMaybe , writeRef @@ -32,10 +31,7 @@ module React.Basic.Hooks , renderRefMaybe , useRef , UseContext - , Context , useContext - , createContext - , contextProvider , UseMemo , useMemo , UseCallback @@ -53,8 +49,7 @@ module React.Basic.Hooks , module Data.Tuple.Nested ) where -import Prelude hiding (bind,discard) - +import Prelude hiding (bind, discard) import Control.Applicative.Indexed (class IxApplicative) import Control.Apply.Indexed (class IxApply) import Control.Bind.Indexed (class IxBind, ibind) @@ -69,90 +64,94 @@ import Effect (Effect) import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, mkEffectFn1, runEffectFn1, runEffectFn2, runEffectFn3) import Prelude (bind) as Prelude import Prim.Row (class Lacks) -import React.Basic (JSX, ReactComponent, empty, keyed, fragment, element, elementKeyed) +import React.Basic (JSX, ReactComponent, ReactContext, Ref, consumer, contextConsumer, contextProvider, createContext, element, elementKeyed, empty, keyed, fragment, provider) import Type.Equality (class TypeEquals) import Unsafe.Coerce (unsafeCoerce) import Unsafe.Reference (unsafeRefEq) -- | Alias for convenience. -type CreateComponent props = Effect (ReactComponent props) +type CreateComponent props + = Effect (ReactComponent props) -- | Create a React component given a display name and render function. -- | Creating components is effectful because React uses the function -- | instance as the component's "identity" or "type". Components should -- | be created during a bootstrap phase and not within component -- | lifecycles or render functions. -component - :: forall hooks props - . Lacks "key" props - => Lacks "ref" props - => String - -> ({ | props } -> Render Unit hooks JSX) - -> CreateComponent { | props } +component :: + forall hooks props. + Lacks "key" props => + Lacks "ref" props => + String -> + ({ | props } -> Render Unit hooks JSX) -> + CreateComponent { | props } component name renderFn = - let c = unsafeReactFunctionComponent (mkEffectFn1 (\props -> case renderFn props of Render a -> a)) - in runEffectFn2 unsafeSetDisplayName name c + let + c = unsafeReactFunctionComponent (mkEffectFn1 (\props -> case renderFn props of Render a -> a)) + in + runEffectFn2 unsafeSetDisplayName name c unsafeReactFunctionComponent :: forall props. EffectFn1 props JSX -> ReactComponent props unsafeReactFunctionComponent = unsafeCoerce -memo - :: forall props - . CreateComponent props - -> CreateComponent props +memo :: + forall props. + CreateComponent props -> + CreateComponent props memo = flip Prelude.bind (runEffectFn1 memo_) foreign import data UseState :: Type -> Type -> Type -useState - :: forall state - . state - -> Hook (UseState state) (state /\ ((state -> state) -> Effect Unit)) -useState initialState = Render do - runEffectFn2 useState_ (mkFn2 Tuple) initialState +useState :: + forall state. + state -> + Hook (UseState state) (state /\ ((state -> state) -> Effect Unit)) +useState initialState = + Render do + runEffectFn2 useState_ (mkFn2 Tuple) initialState foreign import data UseEffect :: Type -> Type -> Type -- | The effect will be run when the component is mounted, and the effect -- | returned from the function will be run on cleanup -useEffect - :: forall key - . Eq key - => key - -> Effect (Effect Unit) - -> Hook (UseEffect key) Unit +useEffect :: + forall key. + Eq key => + key -> + Effect (Effect Unit) -> + Hook (UseEffect key) Unit useEffect key effect = Render (runEffectFn3 useEffect_ (mkFn2 eq) key effect) foreign import data UseLayoutEffect :: Type -> Type -> Type -useLayoutEffect - :: forall key - . Eq key - => key - -> Effect (Effect Unit) - -> Hook (UseLayoutEffect key) Unit +useLayoutEffect :: + forall key. + Eq key => + key -> + Effect (Effect Unit) -> + Hook (UseLayoutEffect key) Unit useLayoutEffect keys effect = Render (runEffectFn3 useLayoutEffect_ (mkFn2 eq) keys effect) foreign import data UseReducer :: Type -> Type -> Type -> Type -useReducer - :: forall state action - . state - -> (state -> action -> state) - -> Hook (UseReducer state action) (state /\ (action -> Effect Unit)) -useReducer initialState reducer = Render do - runEffectFn3 useReducer_ - (mkFn2 Tuple) - (mkFn2 reducer) - initialState +useReducer :: + forall state action. + state -> + (state -> action -> state) -> + Hook (UseReducer state action) (state /\ (action -> Effect Unit)) +useReducer initialState reducer = + Render do + runEffectFn3 useReducer_ + (mkFn2 Tuple) + (mkFn2 reducer) + initialState foreign import data UseRef :: Type -> Type -> Type -foreign import data Ref :: Type -> Type - -useRef :: forall a . a -> Hook (UseRef a) (Ref a) -useRef initialValue = Render do - runEffectFn1 useRef_ initialValue +useRef :: forall a. a -> Hook (UseRef a) (Ref a) +useRef initialValue = + Render do + runEffectFn1 useRef_ initialValue readRef :: forall a. Ref a -> Effect a readRef = runEffectFn1 readRef_ @@ -171,47 +170,43 @@ renderRefMaybe a = Render (readRefMaybe a) foreign import data UseContext :: Type -> Type -> Type -foreign import data Context :: Type -> Type - -useContext :: forall a . Context a -> Hook (UseContext a) (Maybe a) -useContext context = Render (map toMaybe (runEffectFn1 useContext_ context)) - -foreign import createContext :: forall a. Effect (Context a) - -contextProvider :: forall a. Context a -> a -> JSX -> JSX -contextProvider context a child = element (contextProvider_ context) { value: a, children: child } +useContext :: forall a. ReactContext a -> Hook (UseContext a) a +useContext context = Render (runEffectFn1 useContext_ context) foreign import data UseMemo :: Type -> Type -> Type -> Type -useMemo - :: forall key a - . Eq key - => key - -> (Unit -> a) - -> Hook (UseMemo key a) a +useMemo :: + forall key a. + Eq key => + key -> + (Unit -> a) -> + Hook (UseMemo key a) a useMemo key computeA = Render (runEffectFn3 useMemo_ (mkFn2 eq) key computeA) foreign import data UseCallback :: Type -> Type -> Type -> Type -useCallback - :: forall key a - . Eq key - => key - -> a - -> Hook (UseCallback key a) a +useCallback :: + forall key a. + Eq key => + key -> + a -> + Hook (UseCallback key a) a useCallback key computeA = Render (runEffectFn3 useCallback_ (mkFn2 eq) key computeA) foreign import data UseEqCache :: Type -> Type -> Type -useEqCache - :: forall a - . Eq a - => a - -> Hook (UseCallback a a) a +useEqCache :: + forall a. + Eq a => + a -> + Hook (UseCallback a a) a useEqCache a = Render (runEffectFn2 useEqCache_ (mkFn2 eq) a) -newtype UnsafeReference a = UnsafeReference a +newtype UnsafeReference a + = UnsafeReference a + derive instance newtypeUnsafeReference :: Newtype (UnsafeReference a) _ + instance eqUnsafeReference :: Eq (UnsafeReference a) where eq = unsafeRefEq @@ -219,11 +214,14 @@ instance eqUnsafeReference :: Eq (UnsafeReference a) where -- | body, i.e. during "render". This includes hooks and ends with -- | returning JSX (see `pure`), but does not allow arbitrary side -- | effects. -newtype Render x y a = Render (Effect a) +newtype Render x y a + = Render (Effect a) -type Pure a = forall hooks. Render hooks hooks a +type Pure a + = forall hooks. Render hooks hooks a -type Hook (newHook :: Type -> Type) a = forall hooks. Render hooks (newHook hooks) a +type Hook (newHook :: Type -> Type) a + = forall hooks. Render hooks (newHook hooks) a instance ixFunctorRender :: IxFunctor Render where imap f (Render a) = Render (map f a) @@ -267,106 +265,99 @@ instance monoidRender :: (TypeEquals x y, Monoid a) => Monoid (Render x y a) whe -- | error messages in logs. -- | -- | __*See also:* `component`__ -foreign import displayName - :: forall props - . ReactComponent props - -> String - +foreign import displayName :: + forall props. + ReactComponent props -> + String -- | -- | Internal utility or FFI functions -- | - -foreign import memo_ - :: forall props - . EffectFn1 - (ReactComponent props) - (ReactComponent props) - -foreign import unsafeSetDisplayName - :: forall props - . EffectFn2 String (ReactComponent props) (ReactComponent props) - -foreign import useState_ - :: forall state - . EffectFn2 - (forall a b. Fn2 a b (a /\ b)) - state - (state /\ ((state -> state) -> Effect Unit)) - -foreign import useEffect_ - :: forall key - . EffectFn3 - (Fn2 key key Boolean) - key - (Effect (Effect Unit)) - Unit - -foreign import useLayoutEffect_ - :: forall key - . EffectFn3 - (Fn2 key key Boolean) - key - (Effect (Effect Unit)) - Unit - -foreign import useReducer_ - :: forall state action - . EffectFn3 - (forall a b. Fn2 a b (a /\ b)) - (Fn2 state action state) - state - (state /\ (action -> Effect Unit)) - -foreign import readRef_ - :: forall a - . EffectFn1 - (Ref a) - a - -foreign import writeRef_ - :: forall a - . EffectFn2 - (Ref a) - a - Unit - -foreign import useRef_ - :: forall a - . EffectFn1 - a - (Ref a) - -foreign import useContext_ - :: forall a - . EffectFn1 - (Context a) - (Nullable a) - -foreign import contextProvider_ - :: forall a - . Context a - -> ReactComponent { value :: a, children :: JSX } - -foreign import useMemo_ - :: forall key a - . EffectFn3 - (Fn2 key key Boolean) - key - (Unit -> a) - a - -foreign import useCallback_ - :: forall key a - . EffectFn3 - (Fn2 key key Boolean) - key - a - a - -foreign import useEqCache_ - :: forall a - . EffectFn2 - (Fn2 a a Boolean) - a - a +foreign import memo_ :: + forall props. + EffectFn1 + (ReactComponent props) + (ReactComponent props) + +foreign import unsafeSetDisplayName :: + forall props. + EffectFn2 String (ReactComponent props) (ReactComponent props) + +foreign import useState_ :: + forall state. + EffectFn2 + (forall a b. Fn2 a b (a /\ b)) + state + (state /\ ((state -> state) -> Effect Unit)) + +foreign import useEffect_ :: + forall key. + EffectFn3 + (Fn2 key key Boolean) + key + (Effect (Effect Unit)) + Unit + +foreign import useLayoutEffect_ :: + forall key. + EffectFn3 + (Fn2 key key Boolean) + key + (Effect (Effect Unit)) + Unit + +foreign import useReducer_ :: + forall state action. + EffectFn3 + (forall a b. Fn2 a b (a /\ b)) + (Fn2 state action state) + state + (state /\ (action -> Effect Unit)) + +foreign import readRef_ :: + forall a. + EffectFn1 + (Ref a) + a + +foreign import writeRef_ :: + forall a. + EffectFn2 + (Ref a) + a + Unit + +foreign import useRef_ :: + forall a. + EffectFn1 + a + (Ref a) + +foreign import useContext_ :: + forall a. + EffectFn1 + (ReactContext a) + a + +foreign import useMemo_ :: + forall key a. + EffectFn3 + (Fn2 key key Boolean) + key + (Unit -> a) + a + +foreign import useCallback_ :: + forall key a. + EffectFn3 + (Fn2 key key Boolean) + key + a + a + +foreign import useEqCache_ :: + forall a. + EffectFn2 + (Fn2 a a Boolean) + a + a