From 90e61f0151a35333f238d37b9a71fa92472346a8 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sun, 25 Nov 2018 09:47:50 -0500 Subject: [PATCH 01/17] Initial hooks implementation --- bower.json | 3 +- src/React.js | 10 ++ src/React.purs | 55 ++++++++ src/React/Hooks.js | 91 +++++++++++++ src/React/Hooks.purs | 300 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 458 insertions(+), 1 deletion(-) create mode 100644 src/React/Hooks.js create mode 100644 src/React/Hooks.purs diff --git a/bower.json b/bower.json index 2a1d79e..7089497 100644 --- a/bower.json +++ b/bower.json @@ -23,7 +23,8 @@ "purescript-exceptions": "^4.0.0", "purescript-maybe": "^4.0.0", "purescript-nullable": "^4.0.0", - "purescript-typelevel-prelude": "^3.0.0" + "purescript-typelevel-prelude": "^3.0.0", + "purescript-tuples": "^5.1.0" }, "devDependencies": { "purescript-console": "^4.1.0", diff --git a/src/React.js b/src/React.js index db2c58b..184b5c6 100644 --- a/src/React.js +++ b/src/React.js @@ -169,3 +169,13 @@ function createContext(defaultValue) { }; } exports.createContext = createContext; + +exports.createElementHooks = function createElementHooks(props) { + return function(fn) { + var fn_ = function(props) { + return fn(props)(); + }; + + return React.createElement(fn_, props); + }; +}; diff --git a/src/React.purs b/src/React.purs index e7b2d0e..989b0ae 100644 --- a/src/React.purs +++ b/src/React.purs @@ -63,6 +63,7 @@ module React , ContextProvider , ContextConsumer , createContext + , module React ) where import Prelude @@ -75,6 +76,53 @@ import Prim.Row as Row import Type.Row (type (+)) import Unsafe.Coerce (unsafeCoerce) +import React.Hooks + ( useState + , useStateLazy + , setState + , modifyState + , SetState + + , useEffect + , effectInput + , EffectInput + + , useContext + , Context + + , useReducer + , useReducerLazy + , dispatch + , Dispatch + , Action_ + , Action + + , useCallback + , callbackInput + , CallbackInput + + , useMemo + , memoInput + , MemoInput + + , useRef + , getRef + , setRef + , Ref + + , useImperativeMethods + , imperativeMethodsInput + , ImperativeMethodsInput + + , useMutationEffect + , mutationEffectInput + , MutationEffectInput + + , useLayoutEffect + , layoutEffectInput + , LayoutEffectInput + ) as React + -- | Name of a tag. type TagName = String @@ -463,3 +511,10 @@ type ContextConsumer a = ReactClass { children :: a -> ReactElement } -- | Create a new context provider/consumer pair given a default value. foreign import createContext :: forall a. a -> Context a + +-- | Create an element from a function using Hooks. +foreign import createElementHooks + :: forall props + . { | props } + -> ({ | props } -> Effect ReactElement) + -> ReactElement diff --git a/src/React/Hooks.js b/src/React/Hooks.js new file mode 100644 index 0000000..90562bc --- /dev/null +++ b/src/React/Hooks.js @@ -0,0 +1,91 @@ +'use strict'; + +var React = require('react'); + +exports.useState_ = function useState_(Tuple, initialState) { + var result = React.useState(initialState); + + var state = result[0]; + + var setState = result[1]; + + var tuple = Tuple(state)(setState); + + return tuple; +}; + +exports.useEffect_ = function useEffect_(effect, inputs) { + var result = inputs ? React.useEffect(effect, inputs) : React.useEffect(effect); + + return result; +}; + +exports.useContext_ = function useContext_(context) { + var result = React.useContext(context); + + return result; +} + +exports.useReducer_ = function useReducer_(Tuple, reducer, initialState) { + var result = React.useReducer(reducer, initialState); + + var state = result[0]; + + var dispatch = result[1]; + + var tuple = Tuple(state)(dispatch); + + return tuple; +}; + +exports.useReducerLazy_ = function useReducerLazy_(Tuple, reducer, initialState, initialAction) { + var result = React.useReducer(reducer, initialState, initialAction); + + var state = result[0]; + + var dispatch = result[1]; + + var tuple = Tuple(state)(dispatch); + + return tuple; +}; + +exports.useCallback_ = function useCallback_(callback, inputs) { + var result = inputs ? React.useCallback(callback, inputs) : React.useCallback(callback); + + return result; +}; + +exports.useMemo_ = function useMemo_(memo, inputs) { + var result = inputs ? React.useMemo(memo, inputs) : React.useMemo(memo); + + return result; +}; + +exports.useRef_ = function useRef_(initialValue) { + var result = React.useRef(initialValue); + + return result; +} + +exports.setRef_ = function setRef_(ref, value) { + ref.current = value; +} + +exports.useImperativeMethods_ = function useImperativeMethods_(ref, imperativeMethods, inputs) { + var result = inputs ? React.useImperativeMethods(ref, imperativeMethods, inputs) : React.useImperativeMethods(ref, imperativeMethods); + + return result; +}; + +exports.useMutationEffect_ = function useMutationEffect_(mutationEffect, inputs) { + var result = inputs ? React.useMutationEffect(mutationEffect, inputs) : React.useMutationEffect(mutationEffect); + + return result; +}; + +exports.useLayoutEffect_ = function useLayoutEffect_(layoutEffect, inputs) { + var result = inputs ? React.useLayoutEffect(layoutEffect, inputs) : React.useLayoutEffect(layoutEffect); + + return result; +}; diff --git a/src/React/Hooks.purs b/src/React/Hooks.purs new file mode 100644 index 0000000..0d2536a --- /dev/null +++ b/src/React/Hooks.purs @@ -0,0 +1,300 @@ +module React.Hooks + ( useState + , useStateLazy + , setState + , modifyState + , SetState + + , useEffect + , effectInput + , EffectInput + + , useContext + , Context + + , useReducer + , useReducerLazy + , dispatch + , Dispatch + , Action_ + , Action + + , useCallback + , callbackInput + , CallbackInput + + , useMemo + , memoInput + , MemoInput + + , useRef + , getRef + , setRef + , Ref + + , useImperativeMethods + , imperativeMethodsInput + , ImperativeMethodsInput + + , useMutationEffect + , mutationEffectInput + , MutationEffectInput + + , useLayoutEffect + , layoutEffectInput + , LayoutEffectInput + ) where + +import Prelude + +import Data.Function.Uncurried (Fn2, mkFn2) +import Data.Maybe (Maybe) +import Data.Nullable (Nullable) +import Data.Nullable as Nullable +import Data.Tuple (Tuple(..)) + +import Effect (Effect) + +import Effect.Uncurried + ( EffectFn1 + , EffectFn2 + , EffectFn3 + , EffectFn4 + , runEffectFn1 + , runEffectFn2 + , runEffectFn3 + , runEffectFn4 + ) + +import Unsafe.Coerce (unsafeCoerce) + +useState + :: forall a + . a + -> Effect (Tuple a (SetState a)) +useState = runEffectFn2 useState_ Tuple + +useStateLazy + :: forall a + . (Unit -> a) + -> Effect (Tuple a (SetState a)) +useStateLazy = runEffectFn2 useState_ Tuple + +setState + :: forall a + . SetState a + -> a + -> Effect Unit +setState k = runEffectFn1 k' + where + k' :: EffectFn1 a Unit + k' = unsafeCoerce k + +modifyState + :: forall a + . SetState a + -> (a -> a) + -> Effect Unit +modifyState k = runEffectFn1 k' + where + k' :: EffectFn1 (a -> a) Unit + k' = unsafeCoerce k + +foreign import data SetState :: Type -> Type + +foreign import useState_ + :: forall a b + . EffectFn2 (b -> SetState b -> Tuple b (SetState b)) + a + (Tuple b (SetState b)) + +useEffect + :: forall a + . Effect (Effect a) + -> Maybe (Array EffectInput) + -> Effect Unit +useEffect k = runEffectFn2 useEffect_ k <<< Nullable.toNullable + +effectInput :: forall a. a -> EffectInput +effectInput = unsafeCoerce + +foreign import data EffectInput :: Type + +foreign import useEffect_ + :: forall a + . EffectFn2 (Effect (Effect a)) + (Nullable (Array EffectInput)) + Unit + +useContext :: forall a. Context a -> Effect a +useContext = runEffectFn1 useContext_ + +foreign import data Context :: Type -> Type + +foreign import useContext_ + :: forall a + . EffectFn1 (Context a) + a + +useReducer + :: forall r a + . (a -> Action r -> a) + -> a + -> Effect (Tuple a (Dispatch (Action r))) +useReducer = runEffectFn3 useReducer_ Tuple <<< mkFn2 + +useReducerLazy + :: forall r a + . (a -> Action r -> a) + -> a + -> Action r + -> Effect (Tuple a (Dispatch (Action r))) +useReducerLazy = runEffectFn4 useReducerLazy_ Tuple <<< mkFn2 + +dispatch + :: forall r + . Dispatch (Action r) + -> Action r + -> Effect Unit +dispatch k = runEffectFn1 k' + where + k' :: EffectFn1 (Action r) Unit + k' = unsafeCoerce k + +foreign import data Dispatch :: Type -> Type + +foreign import data Action_ :: # Type -> Type + +type Action r = Action_ ( type :: String | r ) + +foreign import useReducer_ + :: forall r a + . EffectFn3 (a -> Dispatch (Action r) -> Tuple a (Dispatch (Action r))) + (Fn2 a (Action r) a) + a + (Tuple a (Dispatch (Action r))) + +foreign import useReducerLazy_ + :: forall r a + . EffectFn4 (a -> Dispatch (Action r) -> Tuple a (Dispatch (Action r))) + (Fn2 a (Action r) a) + a + (Action r) + (Tuple a (Dispatch (Action r))) + +useCallback + :: forall a b + . (a -> b) + -> Maybe (Array CallbackInput) + -> Effect (a -> b) +useCallback k = runEffectFn2 useCallback_ k <<< Nullable.toNullable + +callbackInput :: forall a. a -> CallbackInput +callbackInput = unsafeCoerce + +foreign import data CallbackInput :: Type + +foreign import useCallback_ + :: forall a b + . EffectFn2 (a -> b) + (Nullable (Array CallbackInput)) + (a -> b) + +useMemo + :: forall a b + . (Unit -> a -> b) + -> Maybe (Array MemoInput) + -> Effect (a -> b) +useMemo k = runEffectFn2 useMemo_ k <<< Nullable.toNullable + +memoInput :: forall a. a -> MemoInput +memoInput = unsafeCoerce + +foreign import data MemoInput :: Type + +foreign import useMemo_ + :: forall a b + . EffectFn2 (Unit -> a -> b) + (Nullable (Array MemoInput)) + (a -> b) + +useRef :: forall a. Maybe a -> Effect (Ref a) +useRef = runEffectFn1 useRef_ <<< Nullable.toNullable + +getRef :: forall a. Ref a -> Maybe a +getRef r = Nullable.toMaybe r'.current + where + r' :: { current :: Nullable a } + r' = unsafeCoerce r + +setRef :: forall a. Ref a -> Maybe a -> Effect Unit +setRef r = runEffectFn2 setRef_ r <<< Nullable.toNullable + +foreign import data Ref :: Type -> Type + +foreign import useRef_ + :: forall a + . EffectFn1 (Nullable a) + (Ref a) + +foreign import setRef_ + :: forall a + . EffectFn2 (Ref a) + (Nullable a) + Unit + +useImperativeMethods + :: forall r a + . Ref a + -> (Unit -> { | r }) + -> Maybe (Array ImperativeMethodsInput) + -> Effect Unit +useImperativeMethods a k = runEffectFn3 useImperativeMethods_ a k <<< Nullable.toNullable + +imperativeMethodsInput :: forall a. a -> ImperativeMethodsInput +imperativeMethodsInput = unsafeCoerce + +foreign import data ImperativeMethodsInput :: Type + +foreign import useImperativeMethods_ + :: forall r a + . EffectFn3 (Ref a) + (Unit -> { | r }) + (Nullable (Array ImperativeMethodsInput)) + Unit + +useMutationEffect + :: forall a + . Effect (Effect a) + -> Maybe (Array MutationEffectInput) + -> Effect Unit +useMutationEffect k = runEffectFn2 useMutationEffect_ k <<< Nullable.toNullable + +mutationEffectInput :: forall a. a -> MutationEffectInput +mutationEffectInput = unsafeCoerce + +foreign import data MutationEffectInput :: Type + +foreign import useMutationEffect_ + :: forall a + . EffectFn2 (Effect (Effect a)) + (Nullable (Array MutationEffectInput)) + Unit + +useLayoutEffect + :: forall a + . Effect (Effect a) + -> Maybe (Array LayoutEffectInput) + -> Effect Unit +useLayoutEffect k = runEffectFn2 useLayoutEffect_ k <<< Nullable.toNullable + +layoutEffectInput :: forall a. a -> LayoutEffectInput +layoutEffectInput = unsafeCoerce + +foreign import data LayoutEffectInput :: Type + +foreign import useLayoutEffect_ + :: forall a + . EffectFn2 (Effect (Effect a)) + (Nullable (Array LayoutEffectInput)) + Unit From 0e5d81bf986a8cc750dfa6e67e041fa114e149d9 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sun, 25 Nov 2018 13:27:53 -0500 Subject: [PATCH 02/17] Remove Action type --- src/React/Hooks.purs | 48 +++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/src/React/Hooks.purs b/src/React/Hooks.purs index 0d2536a..23c02e2 100644 --- a/src/React/Hooks.purs +++ b/src/React/Hooks.purs @@ -16,8 +16,6 @@ module React.Hooks , useReducerLazy , dispatch , Dispatch - , Action_ - , Action , useCallback , callbackInput @@ -137,50 +135,46 @@ foreign import useContext_ a useReducer - :: forall r a - . (a -> Action r -> a) + :: forall a b + . (a -> b -> a) -> a - -> Effect (Tuple a (Dispatch (Action r))) + -> Effect (Tuple a (Dispatch a b)) useReducer = runEffectFn3 useReducer_ Tuple <<< mkFn2 useReducerLazy - :: forall r a - . (a -> Action r -> a) + :: forall a b + . (a -> b -> a) -> a - -> Action r - -> Effect (Tuple a (Dispatch (Action r))) + -> b + -> Effect (Tuple a (Dispatch a b)) useReducerLazy = runEffectFn4 useReducerLazy_ Tuple <<< mkFn2 dispatch - :: forall r - . Dispatch (Action r) - -> Action r + :: forall a b + . Dispatch a b + -> a -> Effect Unit dispatch k = runEffectFn1 k' where - k' :: EffectFn1 (Action r) Unit + k' :: EffectFn1 a Unit k' = unsafeCoerce k -foreign import data Dispatch :: Type -> Type - -foreign import data Action_ :: # Type -> Type - -type Action r = Action_ ( type :: String | r ) +foreign import data Dispatch :: Type -> Type -> Type foreign import useReducer_ - :: forall r a - . EffectFn3 (a -> Dispatch (Action r) -> Tuple a (Dispatch (Action r))) - (Fn2 a (Action r) a) + :: forall a b + . EffectFn3 (a -> Dispatch a b -> Tuple a (Dispatch a b)) + (Fn2 a b a) a - (Tuple a (Dispatch (Action r))) + (Tuple a (Dispatch a b)) foreign import useReducerLazy_ - :: forall r a - . EffectFn4 (a -> Dispatch (Action r) -> Tuple a (Dispatch (Action r))) - (Fn2 a (Action r) a) + :: forall a b + . EffectFn4 (a -> Dispatch a b -> Tuple a (Dispatch a b)) + (Fn2 a b a) a - (Action r) - (Tuple a (Dispatch (Action r))) + b + (Tuple a (Dispatch a b)) useCallback :: forall a b From 108821817dfe571a92a51a2f82c751d0de6fe530 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sun, 25 Nov 2018 13:29:14 -0500 Subject: [PATCH 03/17] Fix remove Action --- src/React.purs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/React.purs b/src/React.purs index 989b0ae..e777313 100644 --- a/src/React.purs +++ b/src/React.purs @@ -94,8 +94,6 @@ import React.Hooks , useReducerLazy , dispatch , Dispatch - , Action_ - , Action , useCallback , callbackInput From 53d8011fb754ec8a8089c173fa121284cca322e3 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sun, 25 Nov 2018 13:50:09 -0500 Subject: [PATCH 04/17] Flip createElementHooks arguments --- src/React.js | 14 +++++++++----- src/React.purs | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/React.js b/src/React.js index 184b5c6..c877f84 100644 --- a/src/React.js +++ b/src/React.js @@ -170,12 +170,16 @@ function createContext(defaultValue) { } exports.createContext = createContext; -exports.createElementHooks = function createElementHooks(props) { - return function(fn) { - var fn_ = function(props) { - return fn(props)(); +exports.createElementHooks = function createElementHooks(fn) { + return function(props) { + var name = fn.name; + + var fn_ = {}; + + fn_[name] = function(props_) { + return fn(props_)(); }; - return React.createElement(fn_, props); + return React.createElement(fn_[name], props); }; }; diff --git a/src/React.purs b/src/React.purs index e777313..a20ea0e 100644 --- a/src/React.purs +++ b/src/React.purs @@ -513,6 +513,6 @@ foreign import createContext :: forall a. a -> Context a -- | Create an element from a function using Hooks. foreign import createElementHooks :: forall props - . { | props } - -> ({ | props } -> Effect ReactElement) + . ({ | props } -> Effect ReactElement) + -> { | props } -> ReactElement From 102d0449a6330fb92444f05341900166cfd9c6c4 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sun, 25 Nov 2018 15:20:05 -0500 Subject: [PATCH 05/17] Remove re-export --- src/React.js | 11 +++++++---- src/React.purs | 46 ---------------------------------------------- 2 files changed, 7 insertions(+), 50 deletions(-) diff --git a/src/React.js b/src/React.js index c877f84..9b8d173 100644 --- a/src/React.js +++ b/src/React.js @@ -174,12 +174,15 @@ exports.createElementHooks = function createElementHooks(fn) { return function(props) { var name = fn.name; - var fn_ = {}; - - fn_[name] = function(props_) { + var fn_ = function(props_) { return fn(props_)(); }; - return React.createElement(fn_[name], props); + Object.defineProperty(fn_, 'name', { + value: name, + writable: false + }); + + return React.createElement(fn_, props); }; }; diff --git a/src/React.purs b/src/React.purs index a20ea0e..62b189d 100644 --- a/src/React.purs +++ b/src/React.purs @@ -63,7 +63,6 @@ module React , ContextProvider , ContextConsumer , createContext - , module React ) where import Prelude @@ -76,51 +75,6 @@ import Prim.Row as Row import Type.Row (type (+)) import Unsafe.Coerce (unsafeCoerce) -import React.Hooks - ( useState - , useStateLazy - , setState - , modifyState - , SetState - - , useEffect - , effectInput - , EffectInput - - , useContext - , Context - - , useReducer - , useReducerLazy - , dispatch - , Dispatch - - , useCallback - , callbackInput - , CallbackInput - - , useMemo - , memoInput - , MemoInput - - , useRef - , getRef - , setRef - , Ref - - , useImperativeMethods - , imperativeMethodsInput - , ImperativeMethodsInput - - , useMutationEffect - , mutationEffectInput - , MutationEffectInput - - , useLayoutEffect - , layoutEffectInput - , LayoutEffectInput - ) as React - -- | Name of a tag. type TagName = String From a60a253222917a65d368a00d10eeab01979c9ae9 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 1 Dec 2018 09:25:54 -0500 Subject: [PATCH 06/17] Use Hook type --- src/React.js | 13 +-- src/React.purs | 4 +- src/React/Hooks.purs | 185 +++++++++++++++++++++++-------------------- 3 files changed, 103 insertions(+), 99 deletions(-) diff --git a/src/React.js b/src/React.js index 9b8d173..e67c23a 100644 --- a/src/React.js +++ b/src/React.js @@ -172,17 +172,6 @@ exports.createContext = createContext; exports.createElementHooks = function createElementHooks(fn) { return function(props) { - var name = fn.name; - - var fn_ = function(props_) { - return fn(props_)(); - }; - - Object.defineProperty(fn_, 'name', { - value: name, - writable: false - }); - - return React.createElement(fn_, props); + return React.createElement(fn, props); }; }; diff --git a/src/React.purs b/src/React.purs index 62b189d..3648d5b 100644 --- a/src/React.purs +++ b/src/React.purs @@ -75,6 +75,8 @@ import Prim.Row as Row import Type.Row (type (+)) import Unsafe.Coerce (unsafeCoerce) +import React.Hooks (Hook) + -- | Name of a tag. type TagName = String @@ -467,6 +469,6 @@ foreign import createContext :: forall a. a -> Context a -- | Create an element from a function using Hooks. foreign import createElementHooks :: forall props - . ({ | props } -> Effect ReactElement) + . ({ | props } -> Hook ReactElement) -> { | props } -> ReactElement diff --git a/src/React/Hooks.purs b/src/React/Hooks.purs index 23c02e2..da05d7f 100644 --- a/src/React/Hooks.purs +++ b/src/React/Hooks.purs @@ -1,5 +1,7 @@ module React.Hooks - ( useState + ( Hook + + , useState , useStateLazy , setState , modifyState @@ -45,7 +47,7 @@ module React.Hooks import Prelude -import Data.Function.Uncurried (Fn2, mkFn2) +import Data.Function.Uncurried (Fn1, Fn2, Fn3, Fn4, mkFn2, runFn1, runFn2, runFn3, runFn4) import Data.Maybe (Maybe) import Data.Nullable (Nullable) import Data.Nullable as Nullable @@ -53,65 +55,54 @@ import Data.Tuple (Tuple(..)) import Effect (Effect) -import Effect.Uncurried - ( EffectFn1 - , EffectFn2 - , EffectFn3 - , EffectFn4 - , runEffectFn1 - , runEffectFn2 - , runEffectFn3 - , runEffectFn4 - ) - import Unsafe.Coerce (unsafeCoerce) useState :: forall a . a - -> Effect (Tuple a (SetState a)) -useState = runEffectFn2 useState_ Tuple + -> Hook (Tuple a (SetState a)) +useState = runFn2 useState_ Tuple useStateLazy :: forall a . (Unit -> a) - -> Effect (Tuple a (SetState a)) -useStateLazy = runEffectFn2 useState_ Tuple + -> Hook (Tuple a (SetState a)) +useStateLazy = runFn2 useState_ Tuple setState :: forall a . SetState a -> a - -> Effect Unit -setState k = runEffectFn1 k' + -> Hook Unit +setState k = runFn1 k' where - k' :: EffectFn1 a Unit + k' :: Fn1 a (Hook Unit) k' = unsafeCoerce k modifyState :: forall a . SetState a -> (a -> a) - -> Effect Unit -modifyState k = runEffectFn1 k' + -> Hook Unit +modifyState k = runFn1 k' where - k' :: EffectFn1 (a -> a) Unit + k' :: Fn1 (a -> a) (Hook Unit) k' = unsafeCoerce k foreign import data SetState :: Type -> Type foreign import useState_ :: forall a b - . EffectFn2 (b -> SetState b -> Tuple b (SetState b)) - a - (Tuple b (SetState b)) + . Fn2 (b -> SetState b -> Tuple b (SetState b)) + a + (Hook (Tuple b (SetState b))) useEffect :: forall a . Effect (Effect a) -> Maybe (Array EffectInput) - -> Effect Unit -useEffect k = runEffectFn2 useEffect_ k <<< Nullable.toNullable + -> Hook Unit +useEffect k = runFn2 useEffect_ k <<< Nullable.toNullable effectInput :: forall a. a -> EffectInput effectInput = unsafeCoerce @@ -120,68 +111,68 @@ foreign import data EffectInput :: Type foreign import useEffect_ :: forall a - . EffectFn2 (Effect (Effect a)) - (Nullable (Array EffectInput)) - Unit + . Fn2 (Effect (Effect a)) + (Nullable (Array EffectInput)) + (Hook Unit) -useContext :: forall a. Context a -> Effect a -useContext = runEffectFn1 useContext_ +useContext :: forall a. Context a -> Hook a +useContext = runFn1 useContext_ foreign import data Context :: Type -> Type foreign import useContext_ :: forall a - . EffectFn1 (Context a) - a + . Fn1 (Context a) + (Hook a) useReducer :: forall a b . (a -> b -> a) -> a - -> Effect (Tuple a (Dispatch a b)) -useReducer = runEffectFn3 useReducer_ Tuple <<< mkFn2 + -> Hook (Tuple a (Dispatch a b)) +useReducer = runFn3 useReducer_ Tuple <<< mkFn2 useReducerLazy :: forall a b . (a -> b -> a) -> a -> b - -> Effect (Tuple a (Dispatch a b)) -useReducerLazy = runEffectFn4 useReducerLazy_ Tuple <<< mkFn2 + -> Hook (Tuple a (Dispatch a b)) +useReducerLazy = runFn4 useReducerLazy_ Tuple <<< mkFn2 dispatch :: forall a b . Dispatch a b -> a - -> Effect Unit -dispatch k = runEffectFn1 k' + -> Hook Unit +dispatch k = runFn1 k' where - k' :: EffectFn1 a Unit + k' :: Fn1 a (Hook Unit) k' = unsafeCoerce k foreign import data Dispatch :: Type -> Type -> Type foreign import useReducer_ :: forall a b - . EffectFn3 (a -> Dispatch a b -> Tuple a (Dispatch a b)) - (Fn2 a b a) - a - (Tuple a (Dispatch a b)) + . Fn3 (a -> Dispatch a b -> Tuple a (Dispatch a b)) + (Fn2 a b a) + a + (Hook (Tuple a (Dispatch a b))) foreign import useReducerLazy_ :: forall a b - . EffectFn4 (a -> Dispatch a b -> Tuple a (Dispatch a b)) - (Fn2 a b a) - a - b - (Tuple a (Dispatch a b)) + . Fn4 (a -> Dispatch a b -> Tuple a (Dispatch a b)) + (Fn2 a b a) + a + b + (Hook (Tuple a (Dispatch a b))) useCallback :: forall a b . (a -> b) -> Maybe (Array CallbackInput) - -> Effect (a -> b) -useCallback k = runEffectFn2 useCallback_ k <<< Nullable.toNullable + -> Hook (a -> b) +useCallback k = runFn2 useCallback_ k <<< Nullable.toNullable callbackInput :: forall a. a -> CallbackInput callbackInput = unsafeCoerce @@ -190,16 +181,16 @@ foreign import data CallbackInput :: Type foreign import useCallback_ :: forall a b - . EffectFn2 (a -> b) - (Nullable (Array CallbackInput)) - (a -> b) + . Fn2 (a -> b) + (Nullable (Array CallbackInput)) + (Hook (a -> b)) useMemo :: forall a b . (Unit -> a -> b) -> Maybe (Array MemoInput) - -> Effect (a -> b) -useMemo k = runEffectFn2 useMemo_ k <<< Nullable.toNullable + -> Hook (a -> b) +useMemo k = runFn2 useMemo_ k <<< Nullable.toNullable memoInput :: forall a. a -> MemoInput memoInput = unsafeCoerce @@ -208,12 +199,12 @@ foreign import data MemoInput :: Type foreign import useMemo_ :: forall a b - . EffectFn2 (Unit -> a -> b) - (Nullable (Array MemoInput)) - (a -> b) + . Fn2 (Unit -> a -> b) + (Nullable (Array MemoInput)) + (Hook (a -> b)) -useRef :: forall a. Maybe a -> Effect (Ref a) -useRef = runEffectFn1 useRef_ <<< Nullable.toNullable +useRef :: forall a. Maybe a -> Hook (Ref a) +useRef = runFn1 useRef_ <<< Nullable.toNullable getRef :: forall a. Ref a -> Maybe a getRef r = Nullable.toMaybe r'.current @@ -221,29 +212,29 @@ getRef r = Nullable.toMaybe r'.current r' :: { current :: Nullable a } r' = unsafeCoerce r -setRef :: forall a. Ref a -> Maybe a -> Effect Unit -setRef r = runEffectFn2 setRef_ r <<< Nullable.toNullable +setRef :: forall a. Ref a -> Maybe a -> Hook Unit +setRef r = runFn2 setRef_ r <<< Nullable.toNullable foreign import data Ref :: Type -> Type foreign import useRef_ :: forall a - . EffectFn1 (Nullable a) - (Ref a) + . Fn1 (Nullable a) + (Hook (Ref a)) foreign import setRef_ :: forall a - . EffectFn2 (Ref a) - (Nullable a) - Unit + . Fn2 (Ref a) + (Nullable a) + (Hook Unit) useImperativeMethods :: forall r a . Ref a -> (Unit -> { | r }) -> Maybe (Array ImperativeMethodsInput) - -> Effect Unit -useImperativeMethods a k = runEffectFn3 useImperativeMethods_ a k <<< Nullable.toNullable + -> Hook Unit +useImperativeMethods a k = runFn3 useImperativeMethods_ a k <<< Nullable.toNullable imperativeMethodsInput :: forall a. a -> ImperativeMethodsInput imperativeMethodsInput = unsafeCoerce @@ -252,17 +243,17 @@ foreign import data ImperativeMethodsInput :: Type foreign import useImperativeMethods_ :: forall r a - . EffectFn3 (Ref a) - (Unit -> { | r }) - (Nullable (Array ImperativeMethodsInput)) - Unit + . Fn3 (Ref a) + (Unit -> { | r }) + (Nullable (Array ImperativeMethodsInput)) + (Hook Unit) useMutationEffect :: forall a . Effect (Effect a) -> Maybe (Array MutationEffectInput) - -> Effect Unit -useMutationEffect k = runEffectFn2 useMutationEffect_ k <<< Nullable.toNullable + -> Hook Unit +useMutationEffect k = runFn2 useMutationEffect_ k <<< Nullable.toNullable mutationEffectInput :: forall a. a -> MutationEffectInput mutationEffectInput = unsafeCoerce @@ -271,16 +262,16 @@ foreign import data MutationEffectInput :: Type foreign import useMutationEffect_ :: forall a - . EffectFn2 (Effect (Effect a)) - (Nullable (Array MutationEffectInput)) - Unit + . Fn2 (Effect (Effect a)) + (Nullable (Array MutationEffectInput)) + (Hook Unit) useLayoutEffect :: forall a . Effect (Effect a) -> Maybe (Array LayoutEffectInput) - -> Effect Unit -useLayoutEffect k = runEffectFn2 useLayoutEffect_ k <<< Nullable.toNullable + -> Hook Unit +useLayoutEffect k = runFn2 useLayoutEffect_ k <<< Nullable.toNullable layoutEffectInput :: forall a. a -> LayoutEffectInput layoutEffectInput = unsafeCoerce @@ -289,6 +280,28 @@ foreign import data LayoutEffectInput :: Type foreign import useLayoutEffect_ :: forall a - . EffectFn2 (Effect (Effect a)) - (Nullable (Array LayoutEffectInput)) - Unit + . Fn2 (Effect (Effect a)) + (Nullable (Array LayoutEffectInput)) + (Hook Unit) + +foreign import data Hook :: Type -> Type + +unHook :: forall a. Hook a -> a +unHook = unsafeCoerce + +hook :: forall a. a -> Hook a +hook = unsafeCoerce + +instance functorHook :: Functor Hook where + map k = hook <<< k <<< unHook + +instance applyHook :: Apply Hook where + apply k fa = hook (unHook k (unHook fa)) + +instance applicativeHook :: Applicative Hook where + pure = hook + +instance bindHook :: Bind Hook where + bind fa k = k (unHook fa) + +instance monadHook :: Monad Hook From e29447e6738bc8bae45e6e9f25aad7a966627b0d Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 1 Dec 2018 09:31:39 -0500 Subject: [PATCH 07/17] Export createElementHooks --- src/React.purs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/React.purs b/src/React.purs index 3648d5b..55353f3 100644 --- a/src/React.purs +++ b/src/React.purs @@ -63,6 +63,7 @@ module React , ContextProvider , ContextConsumer , createContext + , createElementHooks ) where import Prelude From e39a96c61576afa1e3bdebc68cd010ef698a3d0b Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 1 Dec 2018 09:33:48 -0500 Subject: [PATCH 08/17] Rename Hooks to Hook --- src/React.js | 2 +- src/React.purs | 8 ++++---- src/React/{Hooks.js => Hook.js} | 0 src/React/{Hooks.purs => Hook.purs} | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename src/React/{Hooks.js => Hook.js} (100%) rename src/React/{Hooks.purs => Hook.purs} (99%) diff --git a/src/React.js b/src/React.js index e67c23a..81f8c69 100644 --- a/src/React.js +++ b/src/React.js @@ -170,7 +170,7 @@ function createContext(defaultValue) { } exports.createContext = createContext; -exports.createElementHooks = function createElementHooks(fn) { +exports.createElementHook = function createElementHook(fn) { return function(props) { return React.createElement(fn, props); }; diff --git a/src/React.purs b/src/React.purs index 55353f3..047c9e7 100644 --- a/src/React.purs +++ b/src/React.purs @@ -63,7 +63,7 @@ module React , ContextProvider , ContextConsumer , createContext - , createElementHooks + , createElementHook ) where import Prelude @@ -76,7 +76,7 @@ import Prim.Row as Row import Type.Row (type (+)) import Unsafe.Coerce (unsafeCoerce) -import React.Hooks (Hook) +import React.Hook (Hook) -- | Name of a tag. type TagName = String @@ -467,8 +467,8 @@ type ContextConsumer a = ReactClass { children :: a -> ReactElement } -- | Create a new context provider/consumer pair given a default value. foreign import createContext :: forall a. a -> Context a --- | Create an element from a function using Hooks. -foreign import createElementHooks +-- | Create an element from a function using Hook. +foreign import createElementHook :: forall props . ({ | props } -> Hook ReactElement) -> { | props } diff --git a/src/React/Hooks.js b/src/React/Hook.js similarity index 100% rename from src/React/Hooks.js rename to src/React/Hook.js diff --git a/src/React/Hooks.purs b/src/React/Hook.purs similarity index 99% rename from src/React/Hooks.purs rename to src/React/Hook.purs index da05d7f..c39bfb2 100644 --- a/src/React/Hooks.purs +++ b/src/React/Hook.purs @@ -1,4 +1,4 @@ -module React.Hooks +module React.Hook ( Hook , useState From 140c3bce4a9b7fa7002cccadf79f49e577414d62 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 1 Dec 2018 10:19:11 -0500 Subject: [PATCH 09/17] Use Effect --- src/React/Hook.js | 4 ++++ src/React/Hook.purs | 41 ++++++++++++++++++++++------------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/React/Hook.js b/src/React/Hook.js index 90562bc..943208c 100644 --- a/src/React/Hook.js +++ b/src/React/Hook.js @@ -68,6 +68,10 @@ exports.useRef_ = function useRef_(initialValue) { return result; } +exports.getRef_ = function getRef_(ref) { + return ref.current; +} + exports.setRef_ = function setRef_(ref, value) { ref.current = value; } diff --git a/src/React/Hook.purs b/src/React/Hook.purs index c39bfb2..b4a78b8 100644 --- a/src/React/Hook.purs +++ b/src/React/Hook.purs @@ -54,6 +54,7 @@ import Data.Nullable as Nullable import Data.Tuple (Tuple(..)) import Effect (Effect) +import Effect.Uncurried (EffectFn1, EffectFn2, runEffectFn1, runEffectFn2) import Unsafe.Coerce (unsafeCoerce) @@ -73,20 +74,20 @@ setState :: forall a . SetState a -> a - -> Hook Unit -setState k = runFn1 k' + -> Effect Unit +setState k = runEffectFn1 k' where - k' :: Fn1 a (Hook Unit) + k' :: EffectFn1 a Unit k' = unsafeCoerce k modifyState :: forall a . SetState a -> (a -> a) - -> Hook Unit -modifyState k = runFn1 k' + -> Effect Unit +modifyState k = runEffectFn1 k' where - k' :: Fn1 (a -> a) (Hook Unit) + k' :: EffectFn1 (a -> a) Unit k' = unsafeCoerce k foreign import data SetState :: Type -> Type @@ -144,10 +145,10 @@ dispatch :: forall a b . Dispatch a b -> a - -> Hook Unit -dispatch k = runFn1 k' + -> Effect Unit +dispatch k = runEffectFn1 k' where - k' :: Fn1 a (Hook Unit) + k' :: EffectFn1 a Unit k' = unsafeCoerce k foreign import data Dispatch :: Type -> Type -> Type @@ -206,14 +207,11 @@ foreign import useMemo_ useRef :: forall a. Maybe a -> Hook (Ref a) useRef = runFn1 useRef_ <<< Nullable.toNullable -getRef :: forall a. Ref a -> Maybe a -getRef r = Nullable.toMaybe r'.current - where - r' :: { current :: Nullable a } - r' = unsafeCoerce r +getRef :: forall a. Ref a -> Effect (Maybe a) +getRef r = Nullable.toMaybe <$> runEffectFn1 getRef_ r -setRef :: forall a. Ref a -> Maybe a -> Hook Unit -setRef r = runFn2 setRef_ r <<< Nullable.toNullable +setRef :: forall a. Ref a -> Maybe a -> Effect Unit +setRef r = runEffectFn2 setRef_ r <<< Nullable.toNullable foreign import data Ref :: Type -> Type @@ -222,11 +220,16 @@ foreign import useRef_ . Fn1 (Nullable a) (Hook (Ref a)) +foreign import getRef_ + :: forall a + . EffectFn1 (Ref a) + (Nullable a) + foreign import setRef_ :: forall a - . Fn2 (Ref a) - (Nullable a) - (Hook Unit) + . EffectFn2 (Ref a) + (Nullable a) + Unit useImperativeMethods :: forall r a From 4e456dacaf18ebcba660ac7fef39b72fee291298 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 1 Dec 2018 18:21:30 -0500 Subject: [PATCH 10/17] Updates based on feedback from @natefaubion --- src/React.js | 6 ----- src/React.purs | 62 ++++++++++++++++++++++++++++++++++++++++++--- src/React/Hook.purs | 52 ++++++++++++++----------------------- 3 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/React.js b/src/React.js index 81f8c69..db2c58b 100644 --- a/src/React.js +++ b/src/React.js @@ -169,9 +169,3 @@ function createContext(defaultValue) { }; } exports.createContext = createContext; - -exports.createElementHook = function createElementHook(fn) { - return function(props) { - return React.createElement(fn, props); - }; -}; diff --git a/src/React.purs b/src/React.purs index 047c9e7..1fe085c 100644 --- a/src/React.purs +++ b/src/React.purs @@ -63,7 +63,12 @@ module React , ContextProvider , ContextConsumer , createContext - , createElementHook + , createHookElement + , unsafeCreateHookElement + , createHookElementDynamic + , unsafeCreateHookElementDynamic + , createHookLeafElement + , unsafeCreateHookLeafElement ) where import Prelude @@ -467,9 +472,58 @@ type ContextConsumer a = ReactClass { children :: a -> ReactElement } -- | Create a new context provider/consumer pair given a default value. foreign import createContext :: forall a. a -> Context a --- | Create an element from a function using Hook. -foreign import createElementHook +-- | Create an element from a function using Hooks spreading the children array. Used when the children are known up front. +createHookElement + :: forall required given + . ReactPropFields required given + => ({ children :: Children | required } -> Hook ReactElement) + -> { | given } + -> Array ReactElement + -> ReactElement +createHookElement k = createElementImpl (unsafeCoerce k) + +-- | An unsafe version of `createHookElement` which does not enforce the reserved properties "key" and "ref". +unsafeCreateHookElement + :: forall props + . ({ children :: Children | props } -> Hook ReactElement) + -> { | props } + -> Array ReactElement + -> ReactElement +unsafeCreateHookElement k = createElementImpl (unsafeCoerce k) + +-- | Create an element from a function using Hooks passing the children array. Used for a dynamic array of children. +createHookElementDynamic + :: forall required given + . ReactPropFields required given + => ({ children :: Children | required } -> Hook ReactElement) + -> { | given } + -> Array ReactElement + -> ReactElement +createHookElementDynamic k = createElementDynamicImpl (unsafeCoerce k) + +-- | An unsafe version of `createHookElementDynamic` which does not enforce the reserved properties "key" and "ref". +unsafeCreateHookElementDynamic :: forall props - . ({ | props } -> Hook ReactElement) + . ({ children :: Children | props } -> Hook ReactElement) -> { | props } + -> Array ReactElement + -> ReactElement +unsafeCreateHookElementDynamic k = createElementDynamicImpl (unsafeCoerce k) + +-- | Create an element from a function using Hooks that does not require children. +createHookLeafElement + :: forall required given + . ReactPropFields required given + => ({ | given } -> Hook ReactElement) + -> { | given } + -> ReactElement +createHookLeafElement k = createLeafElementImpl (unsafeCoerce k) + +-- | An unsafe version of `createHookLeafElement` which does not enforce the reserved +-- | properties "key" and "ref". +unsafeCreateHookLeafElement + :: forall props + . (props -> Hook ReactElement) + -> props -> ReactElement +unsafeCreateHookLeafElement k = createLeafElementImpl (unsafeCoerce k) diff --git a/src/React/Hook.purs b/src/React/Hook.purs index b4a78b8..e32ac07 100644 --- a/src/React/Hook.purs +++ b/src/React/Hook.purs @@ -1,5 +1,7 @@ module React.Hook ( Hook + , HookInput + , hookInput , useState , useStateLazy @@ -8,8 +10,6 @@ module React.Hook , SetState , useEffect - , effectInput - , EffectInput , useContext , Context @@ -20,12 +20,8 @@ module React.Hook , Dispatch , useCallback - , callbackInput - , CallbackInput , useMemo - , memoInput - , MemoInput , useRef , getRef @@ -101,19 +97,14 @@ foreign import useState_ useEffect :: forall a . Effect (Effect a) - -> Maybe (Array EffectInput) + -> Maybe (Array HookInput) -> Hook Unit useEffect k = runFn2 useEffect_ k <<< Nullable.toNullable -effectInput :: forall a. a -> EffectInput -effectInput = unsafeCoerce - -foreign import data EffectInput :: Type - foreign import useEffect_ :: forall a . Fn2 (Effect (Effect a)) - (Nullable (Array EffectInput)) + (Nullable (Array HookInput)) (Hook Unit) useContext :: forall a. Context a -> Hook a @@ -171,37 +162,27 @@ foreign import useReducerLazy_ useCallback :: forall a b . (a -> b) - -> Maybe (Array CallbackInput) + -> Maybe (Array HookInput) -> Hook (a -> b) useCallback k = runFn2 useCallback_ k <<< Nullable.toNullable -callbackInput :: forall a. a -> CallbackInput -callbackInput = unsafeCoerce - -foreign import data CallbackInput :: Type - foreign import useCallback_ :: forall a b . Fn2 (a -> b) - (Nullable (Array CallbackInput)) + (Nullable (Array HookInput)) (Hook (a -> b)) useMemo :: forall a b . (Unit -> a -> b) - -> Maybe (Array MemoInput) + -> Maybe (Array HookInput) -> Hook (a -> b) useMemo k = runFn2 useMemo_ k <<< Nullable.toNullable -memoInput :: forall a. a -> MemoInput -memoInput = unsafeCoerce - -foreign import data MemoInput :: Type - foreign import useMemo_ :: forall a b . Fn2 (Unit -> a -> b) - (Nullable (Array MemoInput)) + (Nullable (Array HookInput)) (Hook (a -> b)) useRef :: forall a. Maybe a -> Hook (Ref a) @@ -289,12 +270,6 @@ foreign import useLayoutEffect_ foreign import data Hook :: Type -> Type -unHook :: forall a. Hook a -> a -unHook = unsafeCoerce - -hook :: forall a. a -> Hook a -hook = unsafeCoerce - instance functorHook :: Functor Hook where map k = hook <<< k <<< unHook @@ -308,3 +283,14 @@ instance bindHook :: Bind Hook where bind fa k = k (unHook fa) instance monadHook :: Monad Hook + +unHook :: forall a. Hook a -> a +unHook = unsafeCoerce + +hook :: forall a. a -> Hook a +hook = unsafeCoerce + +foreign import data HookInput :: Type + +hookInput :: forall a. a -> HookInput +hookInput = unsafeCoerce From 2c1a2cca22dcf1ba3261ebb36e789746862627e3 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 1 Dec 2018 18:33:29 -0500 Subject: [PATCH 11/17] Fix createHookLeafElement --- src/React.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/React.purs b/src/React.purs index 1fe085c..1366e55 100644 --- a/src/React.purs +++ b/src/React.purs @@ -514,7 +514,7 @@ unsafeCreateHookElementDynamic k = createElementDynamicImpl (unsafeCoerce k) createHookLeafElement :: forall required given . ReactPropFields required given - => ({ | given } -> Hook ReactElement) + => ({ | required } -> Hook ReactElement) -> { | given } -> ReactElement createHookLeafElement k = createLeafElementImpl (unsafeCoerce k) From 088d175417dce162b137bb6d27882eebb80ec046 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 1 Dec 2018 18:36:34 -0500 Subject: [PATCH 12/17] Update unsafeCreateHookLeafElement --- src/React.purs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/React.purs b/src/React.purs index 1366e55..b80838e 100644 --- a/src/React.purs +++ b/src/React.purs @@ -523,7 +523,7 @@ createHookLeafElement k = createLeafElementImpl (unsafeCoerce k) -- | properties "key" and "ref". unsafeCreateHookLeafElement :: forall props - . (props -> Hook ReactElement) - -> props + . ({ | props } -> Hook ReactElement) + -> { | props } -> ReactElement unsafeCreateHookLeafElement k = createLeafElementImpl (unsafeCoerce k) From 7b5ec574030f4562f7727fa09aaec0236c0c79a5 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sun, 2 Dec 2018 09:20:11 -0500 Subject: [PATCH 13/17] Update useMemo --- src/React/Hook.purs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/React/Hook.purs b/src/React/Hook.purs index e32ac07..39e6326 100644 --- a/src/React/Hook.purs +++ b/src/React/Hook.purs @@ -173,17 +173,17 @@ foreign import useCallback_ (Hook (a -> b)) useMemo - :: forall a b - . (Unit -> a -> b) + :: forall a + . (Unit -> a) -> Maybe (Array HookInput) - -> Hook (a -> b) + -> Hook a useMemo k = runFn2 useMemo_ k <<< Nullable.toNullable foreign import useMemo_ - :: forall a b - . Fn2 (Unit -> a -> b) + :: forall a + . Fn2 (Unit -> a) (Nullable (Array HookInput)) - (Hook (a -> b)) + (Hook a) useRef :: forall a. Maybe a -> Hook (Ref a) useRef = runFn1 useRef_ <<< Nullable.toNullable From e8b57389e3fc89700e720be4ee9ab0dd351abe16 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sun, 2 Dec 2018 09:30:53 -0500 Subject: [PATCH 14/17] Fix dispatch --- src/React/Hook.purs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/React/Hook.purs b/src/React/Hook.purs index 39e6326..0c7ca32 100644 --- a/src/React/Hook.purs +++ b/src/React/Hook.purs @@ -135,11 +135,11 @@ useReducerLazy = runFn4 useReducerLazy_ Tuple <<< mkFn2 dispatch :: forall a b . Dispatch a b - -> a + -> b -> Effect Unit dispatch k = runEffectFn1 k' where - k' :: EffectFn1 a Unit + k' :: EffectFn1 b Unit k' = unsafeCoerce k foreign import data Dispatch :: Type -> Type -> Type From edcd569f3e227793a086637753462e1ef4b20917 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 8 Dec 2018 15:04:18 -0500 Subject: [PATCH 15/17] Refactor Context --- src/React.js | 13 ------- src/React.purs | 82 +++++++----------------------------------- src/React/Context.js | 18 ++++++++++ src/React/Context.purs | 30 ++++++++++++++++ src/React/Hook.purs | 20 ++++------- src/React/Types.js | 11 ++++++ src/React/Types.purs | 62 ++++++++++++++++++++++++++++++++ 7 files changed, 140 insertions(+), 96 deletions(-) create mode 100644 src/React/Context.js create mode 100644 src/React/Context.purs create mode 100644 src/React/Types.js create mode 100644 src/React/Types.purs diff --git a/src/React.js b/src/React.js index db2c58b..4cb8b50 100644 --- a/src/React.js +++ b/src/React.js @@ -91,10 +91,6 @@ function getProps(this_) { } exports.getProps = getProps; -exports.childrenToArray = React.Children.toArray - -exports.childrenCount = React.Children.count; - function setStateImpl(this_) { return function(state){ return function(){ @@ -160,12 +156,3 @@ function createElementDynamic(class_) { }; exports.createElementDynamicImpl = createElementDynamic; exports.createElementTagNameDynamic = createElementDynamic; - -function createContext(defaultValue) { - var context = React.createContext(defaultValue); - return { - consumer: context.Consumer, - provider: context.Provider - }; -} -exports.createContext = createContext; diff --git a/src/React.purs b/src/React.purs index b80838e..cd400b6 100644 --- a/src/React.purs +++ b/src/React.purs @@ -2,7 +2,6 @@ module React ( TagName - , ReactElement , ReactComponent , ReactThis , ReactUnusedSnapshot @@ -31,8 +30,6 @@ module React , pureComponent , pureComponentWithDerivedState , statelessComponent - , ReactClass - , ReactRef , getProps , getState , setState @@ -53,22 +50,14 @@ module React , unsafeCreateLeafElement , createElementTagName , createElementTagNameDynamic - , Children - , childrenToArray - , childrenCount - , class IsReactElement - , toElement , fragmentWithKey - , Context - , ContextProvider - , ContextConsumer - , createContext , createHookElement , unsafeCreateHookElement , createHookElementDynamic , unsafeCreateHookElementDynamic , createHookLeafElement , unsafeCreateHookLeafElement + , module Exports ) where import Prelude @@ -82,19 +71,21 @@ import Type.Row (type (+)) import Unsafe.Coerce (unsafeCoerce) import React.Hook (Hook) +import React.Types (ReactElement, ReactClass, Children, ReactRef) +import React.Types + ( ReactClass + , ReactElement + , class IsReactElement + , toElement + , Children + , childrenToArray + , childrenCount + , ReactRef + ) as Exports -- | Name of a tag. type TagName = String --- | A virtual DOM node, or component. -foreign import data ReactElement :: Type - -instance semigroupReactElement :: Semigroup ReactElement where - append a b = toElement [ a, b ] - -instance monoidReactElement :: Monoid ReactElement where - mempty = toElement ([] :: Array ReactElement) - -- | A mounted react component foreign import data ReactComponent :: Type @@ -254,15 +245,8 @@ foreign import statelessComponent :: forall props. (Record props -> ReactElement) -> ReactClass (Record props) --- | React class for components. -foreign import data ReactClass :: Type -> Type - foreign import fragment :: ReactClass { children :: Children } --- | Type for React refs. This type is opaque, but you can use `Data.Foreign` --- | and `DOM` to validate the underlying representation. -foreign import data ReactRef :: Type - -- | Read the component props. foreign import getProps :: forall props state. ReactThis props state -> @@ -426,52 +410,10 @@ foreign import createElementTagName :: forall props. foreign import createElementTagNameDynamic :: forall props. TagName -> props -> Array ReactElement -> ReactElement --- | Internal representation for the children elements passed to a component -foreign import data Children :: Type - --- | Internal conversion function from children elements to an array of React elements -foreign import childrenToArray :: Children -> Array ReactElement - --- | Returns the number of children. -foreign import childrenCount :: Children -> Int - -class IsReactElement a where - toElement :: a -> ReactElement - -instance isReactElementString :: IsReactElement String where - toElement = unsafeCoerce - -instance isReactElementNumber :: IsReactElement Number where - toElement = unsafeCoerce - -instance isReactElementInt :: IsReactElement Int where - toElement = unsafeCoerce - -instance isReactElementChildren :: IsReactElement Children where - toElement = unsafeCoerce - -instance isReactElementReactElement :: IsReactElement ReactElement where - toElement = identity - -instance isReactElementArray :: IsReactElement (Array ReactElement) where - toElement = createElement fragment {} - -- | Creates a keyed fragment. fragmentWithKey :: String -> Array ReactElement -> ReactElement fragmentWithKey = createElement fragment <<< { key: _ } -type Context a = - { consumer :: ContextConsumer a - , provider :: ContextProvider a - } - -type ContextProvider a = ReactClass { children :: Children, value :: a } - -type ContextConsumer a = ReactClass { children :: a -> ReactElement } - --- | Create a new context provider/consumer pair given a default value. -foreign import createContext :: forall a. a -> Context a - -- | Create an element from a function using Hooks spreading the children array. Used when the children are known up front. createHookElement :: forall required given diff --git a/src/React/Context.js b/src/React/Context.js new file mode 100644 index 0000000..020a1af --- /dev/null +++ b/src/React/Context.js @@ -0,0 +1,18 @@ +'use strict'; + +var React = require('react'); + +exports.getProvider = function getProvider(context) { + return context.Provider; +}; + +exports.getConsumer = function getConsumer(context) { + return context.Consumer; +}; + +exports.createContext_ = function createContext_(defaultValue, calculateChangedBits) { + return calculateChangedBits ? + React.createContext(defaultValue, calculateChangedBits) : + React.createContext(defaultValue) + ; +}; diff --git a/src/React/Context.purs b/src/React/Context.purs new file mode 100644 index 0000000..6bf5b72 --- /dev/null +++ b/src/React/Context.purs @@ -0,0 +1,30 @@ +module React.Context + ( Context + , createContext + , getProvider + , getConsumer + ) where + +import Prelude + +import Data.Function.Uncurried (Fn2, mkFn2, runFn2) +import Data.Maybe (Maybe) +import Data.Nullable (Nullable) +import Data.Nullable as Nullable + +import React.Types (ReactElement, ReactClass, Children) + +createContext :: forall a. a -> Maybe (a -> a -> Number) -> Context a +createContext a = runFn2 createContext_ a <<< Nullable.toNullable <<< map mkFn2 + +foreign import data Context :: Type -> Type + +foreign import getProvider :: forall a. Context a -> ReactClass { children :: Children, value :: a } + +foreign import getConsumer :: forall a. Context a -> ReactClass { children :: a -> ReactElement } + +foreign import createContext_ + :: forall a + . Fn2 a + (Nullable (Fn2 a a Number)) + (Context a) diff --git a/src/React/Hook.purs b/src/React/Hook.purs index 0c7ca32..3ed7d85 100644 --- a/src/React/Hook.purs +++ b/src/React/Hook.purs @@ -2,40 +2,30 @@ module React.Hook ( Hook , HookInput , hookInput - , useState , useStateLazy , setState , modifyState , SetState - , useEffect - , useContext - , Context - , useReducer , useReducerLazy , dispatch , Dispatch - , useCallback - , useMemo - , useRef , getRef , setRef + , refToReactRef , Ref - , useImperativeMethods , imperativeMethodsInput , ImperativeMethodsInput - , useMutationEffect , mutationEffectInput , MutationEffectInput - , useLayoutEffect , layoutEffectInput , LayoutEffectInput @@ -54,6 +44,9 @@ import Effect.Uncurried (EffectFn1, EffectFn2, runEffectFn1, runEffectFn2) import Unsafe.Coerce (unsafeCoerce) +import React.Context (Context) +import React.Types (ReactRef) + useState :: forall a . a @@ -110,8 +103,6 @@ foreign import useEffect_ useContext :: forall a. Context a -> Hook a useContext = runFn1 useContext_ -foreign import data Context :: Type -> Type - foreign import useContext_ :: forall a . Fn1 (Context a) @@ -188,6 +179,9 @@ foreign import useMemo_ useRef :: forall a. Maybe a -> Hook (Ref a) useRef = runFn1 useRef_ <<< Nullable.toNullable +refToReactRef :: forall a. Ref a -> ReactRef +refToReactRef = unsafeCoerce + getRef :: forall a. Ref a -> Effect (Maybe a) getRef r = Nullable.toMaybe <$> runEffectFn1 getRef_ r diff --git a/src/React/Types.js b/src/React/Types.js new file mode 100644 index 0000000..b95bced --- /dev/null +++ b/src/React/Types.js @@ -0,0 +1,11 @@ +'use strict'; + +var React = require('react'); + +exports.toElementArray = function toElementArray(elements) { + return React.createElement.apply(React, [React.Fragment, { }].concat(elements)); +}; + +exports.childrenToArray = React.Children.toArray + +exports.childrenCount = React.Children.count; diff --git a/src/React/Types.purs b/src/React/Types.purs new file mode 100644 index 0000000..a3a31da --- /dev/null +++ b/src/React/Types.purs @@ -0,0 +1,62 @@ +module React.Types + ( ReactClass + , ReactElement + , class IsReactElement + , toElement + , Children + , childrenToArray + , childrenCount + , ReactRef + ) where + +import Prelude + +import Unsafe.Coerce (unsafeCoerce) + +-- | React class for components. +foreign import data ReactClass :: Type -> Type + +-- | A virtual DOM node, or component. +foreign import data ReactElement :: Type + +instance semigroupReactElement :: Semigroup ReactElement where + append a b = toElement [ a, b ] + +instance monoidReactElement :: Monoid ReactElement where + mempty = toElement ([] :: Array ReactElement) + +class IsReactElement a where + toElement :: a -> ReactElement + +instance isReactElementString :: IsReactElement String where + toElement = unsafeCoerce + +instance isReactElementNumber :: IsReactElement Number where + toElement = unsafeCoerce + +instance isReactElementInt :: IsReactElement Int where + toElement = unsafeCoerce + +instance isReactElementChildren :: IsReactElement Children where + toElement = unsafeCoerce + +instance isReactElementReactElement :: IsReactElement ReactElement where + toElement = identity + +instance isReactElementArray :: IsReactElement (Array ReactElement) where + toElement = toElementArray + +foreign import toElementArray :: Array ReactElement -> ReactElement + +-- | Internal representation for the children elements passed to a component +foreign import data Children :: Type + +-- | Internal conversion function from children elements to an array of React elements +foreign import childrenToArray :: Children -> Array ReactElement + +-- | Returns the number of children. +foreign import childrenCount :: Children -> Int + +-- | Type for React refs. This type is opaque, but you can use `Data.Foreign` +-- | and `DOM` to validate the underlying representation. +foreign import data ReactRef :: Type From ba17b834b2e769de8101aa77a93d88138cfda84b Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 8 Dec 2018 15:34:51 -0500 Subject: [PATCH 16/17] Add createRenderPropsElement context consumer creation --- src/React.purs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/React.purs b/src/React.purs index cd400b6..4e8cb1c 100644 --- a/src/React.purs +++ b/src/React.purs @@ -57,6 +57,7 @@ module React , unsafeCreateHookElementDynamic , createHookLeafElement , unsafeCreateHookLeafElement + , createRenderPropsElement , module Exports ) where @@ -378,7 +379,7 @@ unsafeCreateElementDynamic :: forall props. unsafeCreateElementDynamic = createElementDynamicImpl foreign import createElementImpl :: forall required given children. - ReactClass required -> given -> Array children -> ReactElement + ReactClass required -> given -> children -> ReactElement foreign import createElementDynamicImpl :: forall required given children. ReactClass required -> given -> Array children -> ReactElement @@ -469,3 +470,13 @@ unsafeCreateHookLeafElement -> { | props } -> ReactElement unsafeCreateHookLeafElement k = createLeafElementImpl (unsafeCoerce k) + +-- | Create an element using the [render props pattern](https://reactjs.org/docs/render-props.html#using-props-other-than-render) when the name of the render prop is "children". +createRenderPropsElement + :: forall required given childrenProps + . ReactPropFields required given + => ReactClass { children :: childrenProps -> ReactElement | required } + -> { | given } + -> (childrenProps -> ReactElement) + -> ReactElement +createRenderPropsElement = createElementImpl From ebe5f47220de9c237d2b6c953f6a00d9aad56a00 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 8 Dec 2018 17:13:27 -0500 Subject: [PATCH 17/17] Refactor Refs --- src/React.purs | 7 ++-- src/React/DOM/Props.purs | 9 +++-- src/React/Hook.js | 14 ------- src/React/Hook.purs | 81 +++++----------------------------------- src/React/Ref.js | 19 ++++++++++ src/React/Ref.purs | 54 +++++++++++++++++++++++++++ src/React/Types.purs | 5 --- 7 files changed, 92 insertions(+), 97 deletions(-) create mode 100644 src/React/Ref.js create mode 100644 src/React/Ref.purs diff --git a/src/React.purs b/src/React.purs index 4e8cb1c..b59224a 100644 --- a/src/React.purs +++ b/src/React.purs @@ -63,7 +63,6 @@ module React import Prelude -import Data.Nullable (Nullable) import Effect (Effect) import Effect.Exception (Error) import Effect.Uncurried (EffectFn1) @@ -72,7 +71,8 @@ import Type.Row (type (+)) import Unsafe.Coerce (unsafeCoerce) import React.Hook (Hook) -import React.Types (ReactElement, ReactClass, Children, ReactRef) +import React.Ref (Ref, DOMRef) +import React.Types (ReactElement, ReactClass, Children) import React.Types ( ReactClass , ReactElement @@ -81,7 +81,6 @@ import React.Types , Children , childrenToArray , childrenCount - , ReactRef ) as Exports -- | Name of a tag. @@ -332,7 +331,7 @@ class ReactPropFields (required :: # Type) (given :: # Type) type ReservedReactPropFields r = ( key :: String - , ref :: SyntheticEventHandler (Nullable ReactRef) + , ref :: Ref DOMRef | r ) diff --git a/src/React/DOM/Props.purs b/src/React/DOM/Props.purs index e3b2b50..75002ef 100644 --- a/src/React/DOM/Props.purs +++ b/src/React/DOM/Props.purs @@ -5,7 +5,7 @@ import Prelude import Data.Nullable (Nullable) import Effect (Effect) import Effect.Uncurried (mkEffectFn1) -import React (ReactRef) +import React.Ref (Ref, DOMRef) import React.SyntheticEvent ( SyntheticEvent , SyntheticAnimationEvent @@ -894,8 +894,11 @@ onScrollCapture f = unsafeMkProps "onScrollCapture" (mkEffectFn1 f) onWheelCapture :: (SyntheticWheelEvent -> Effect Unit) -> Props onWheelCapture f = unsafeMkProps "onWheelCapture" (mkEffectFn1 f) -ref :: (Nullable ReactRef -> Effect Unit) -> Props -ref f = unsafeMkProps "ref" (mkEffectFn1 f) +ref :: Ref DOMRef -> Props +ref = unsafeMkProps "ref" + +callbackRef :: (Nullable DOMRef -> Effect Unit) -> Props +callbackRef f = unsafeMkProps "ref" (mkEffectFn1 f) suppressContentEditableWarning :: Boolean -> Props suppressContentEditableWarning = unsafeMkProps "suppressContentEditableWarning" diff --git a/src/React/Hook.js b/src/React/Hook.js index 943208c..189a55f 100644 --- a/src/React/Hook.js +++ b/src/React/Hook.js @@ -68,26 +68,12 @@ exports.useRef_ = function useRef_(initialValue) { return result; } -exports.getRef_ = function getRef_(ref) { - return ref.current; -} - -exports.setRef_ = function setRef_(ref, value) { - ref.current = value; -} - exports.useImperativeMethods_ = function useImperativeMethods_(ref, imperativeMethods, inputs) { var result = inputs ? React.useImperativeMethods(ref, imperativeMethods, inputs) : React.useImperativeMethods(ref, imperativeMethods); return result; }; -exports.useMutationEffect_ = function useMutationEffect_(mutationEffect, inputs) { - var result = inputs ? React.useMutationEffect(mutationEffect, inputs) : React.useMutationEffect(mutationEffect); - - return result; -}; - exports.useLayoutEffect_ = function useLayoutEffect_(layoutEffect, inputs) { var result = inputs ? React.useLayoutEffect(layoutEffect, inputs) : React.useLayoutEffect(layoutEffect); diff --git a/src/React/Hook.purs b/src/React/Hook.purs index 3ed7d85..881df51 100644 --- a/src/React/Hook.purs +++ b/src/React/Hook.purs @@ -16,19 +16,8 @@ module React.Hook , useCallback , useMemo , useRef - , getRef - , setRef - , refToReactRef - , Ref , useImperativeMethods - , imperativeMethodsInput - , ImperativeMethodsInput - , useMutationEffect - , mutationEffectInput - , MutationEffectInput , useLayoutEffect - , layoutEffectInput - , LayoutEffectInput ) where import Prelude @@ -40,12 +29,12 @@ import Data.Nullable as Nullable import Data.Tuple (Tuple(..)) import Effect (Effect) -import Effect.Uncurried (EffectFn1, EffectFn2, runEffectFn1, runEffectFn2) +import Effect.Uncurried (EffectFn1, runEffectFn1) import Unsafe.Coerce (unsafeCoerce) import React.Context (Context) -import React.Types (ReactRef) +import React.Ref (Ref) useState :: forall a @@ -179,87 +168,37 @@ foreign import useMemo_ useRef :: forall a. Maybe a -> Hook (Ref a) useRef = runFn1 useRef_ <<< Nullable.toNullable -refToReactRef :: forall a. Ref a -> ReactRef -refToReactRef = unsafeCoerce - -getRef :: forall a. Ref a -> Effect (Maybe a) -getRef r = Nullable.toMaybe <$> runEffectFn1 getRef_ r - -setRef :: forall a. Ref a -> Maybe a -> Effect Unit -setRef r = runEffectFn2 setRef_ r <<< Nullable.toNullable - -foreign import data Ref :: Type -> Type - foreign import useRef_ :: forall a . Fn1 (Nullable a) (Hook (Ref a)) -foreign import getRef_ - :: forall a - . EffectFn1 (Ref a) - (Nullable a) - -foreign import setRef_ - :: forall a - . EffectFn2 (Ref a) - (Nullable a) - Unit - useImperativeMethods - :: forall r a + :: forall a . Ref a - -> (Unit -> { | r }) - -> Maybe (Array ImperativeMethodsInput) + -> (Unit -> a) + -> Maybe (Array HookInput) -> Hook Unit useImperativeMethods a k = runFn3 useImperativeMethods_ a k <<< Nullable.toNullable -imperativeMethodsInput :: forall a. a -> ImperativeMethodsInput -imperativeMethodsInput = unsafeCoerce - -foreign import data ImperativeMethodsInput :: Type - foreign import useImperativeMethods_ - :: forall r a - . Fn3 (Ref a) - (Unit -> { | r }) - (Nullable (Array ImperativeMethodsInput)) - (Hook Unit) - -useMutationEffect - :: forall a - . Effect (Effect a) - -> Maybe (Array MutationEffectInput) - -> Hook Unit -useMutationEffect k = runFn2 useMutationEffect_ k <<< Nullable.toNullable - -mutationEffectInput :: forall a. a -> MutationEffectInput -mutationEffectInput = unsafeCoerce - -foreign import data MutationEffectInput :: Type - -foreign import useMutationEffect_ :: forall a - . Fn2 (Effect (Effect a)) - (Nullable (Array MutationEffectInput)) + . Fn3 (Ref a) + (Unit -> a) + (Nullable (Array HookInput)) (Hook Unit) useLayoutEffect :: forall a . Effect (Effect a) - -> Maybe (Array LayoutEffectInput) + -> Maybe (Array HookInput) -> Hook Unit useLayoutEffect k = runFn2 useLayoutEffect_ k <<< Nullable.toNullable -layoutEffectInput :: forall a. a -> LayoutEffectInput -layoutEffectInput = unsafeCoerce - -foreign import data LayoutEffectInput :: Type - foreign import useLayoutEffect_ :: forall a . Fn2 (Effect (Effect a)) - (Nullable (Array LayoutEffectInput)) + (Nullable (Array HookInput)) (Hook Unit) foreign import data Hook :: Type -> Type diff --git a/src/React/Ref.js b/src/React/Ref.js new file mode 100644 index 0000000..44540e2 --- /dev/null +++ b/src/React/Ref.js @@ -0,0 +1,19 @@ +'use strict'; + +var React = require('react'); + +exports.createRef = function createRef() { + return React.createRef(); +}; + +exports.forwardRef_ = function forwardRef(render) { + return React.forwardRef(render); +} + +exports.getRef_ = function getRef_(ref) { + return ref.current; +} + +exports.setRef_ = function setRef_(ref, value) { + ref.current = value; +} diff --git a/src/React/Ref.purs b/src/React/Ref.purs new file mode 100644 index 0000000..78101fb --- /dev/null +++ b/src/React/Ref.purs @@ -0,0 +1,54 @@ +module React.Ref + ( Ref + , DOMRef + , createRef + , forwardRef + , getRef + , setRef + ) where + +import Prelude + +import Data.Function.Uncurried (Fn2, mkFn2) +import Data.Maybe (Maybe) +import Data.Nullable (Nullable) +import Data.Nullable as Nullable + +import Effect (Effect) +import Effect.Uncurried (EffectFn1, EffectFn2, runEffectFn1, runEffectFn2) + +import React.Types (ReactClass, ReactElement) + +-- | Type for React refs. +foreign import data Ref :: Type -> Type + +-- | Type for a Ref value that is a React component instance or a DOM element. +-- | This type is opaque, but you can use `Data.Foreign` and `DOM` to validate the underlying representation. +foreign import data DOMRef :: Type + +foreign import createRef :: forall a. Effect (Ref a) + +forwardRef :: forall props a. (props -> Ref a -> ReactElement) -> ReactClass props +forwardRef = forwardRef_ <<< mkFn2 + +foreign import forwardRef_ + :: forall props a + . Fn2 props (Ref a) ReactElement + -> ReactClass props + +getRef :: forall a. Ref a -> Effect (Maybe a) +getRef r = Nullable.toMaybe <$> runEffectFn1 getRef_ r + +foreign import getRef_ + :: forall a + . EffectFn1 (Ref a) + (Nullable a) + +setRef :: forall a. Ref a -> Maybe a -> Effect Unit +setRef r = runEffectFn2 setRef_ r <<< Nullable.toNullable + +foreign import setRef_ + :: forall a + . EffectFn2 (Ref a) + (Nullable a) + Unit diff --git a/src/React/Types.purs b/src/React/Types.purs index a3a31da..74dd9e8 100644 --- a/src/React/Types.purs +++ b/src/React/Types.purs @@ -6,7 +6,6 @@ module React.Types , Children , childrenToArray , childrenCount - , ReactRef ) where import Prelude @@ -56,7 +55,3 @@ foreign import childrenToArray :: Children -> Array ReactElement -- | Returns the number of children. foreign import childrenCount :: Children -> Int - --- | Type for React refs. This type is opaque, but you can use `Data.Foreign` --- | and `DOM` to validate the underlying representation. -foreign import data ReactRef :: Type