Commit 8df9707e authored by James Laver's avatar James Laver

Hook changes in preparation for 0.4.0

parent 2634f579
......@@ -66,7 +66,6 @@ Not in any particular order
1. Safe DOM props:
* Make sure each element only takes the correct props
* Translate some props with minimal and efficient wrapping
2. Refs
* Test forwardRef
3. Context
......@@ -89,6 +88,24 @@ Not in any particular order
<!-- * `R.React.provide` - provider a value through a `Provider` -->
<!-- * `R.React.consume` - consume a value through a `Consumer` -->
### 0.4.0
Breaking:
* `useState` now takes a (pure) `Unit -> s` initialiser
* `useState`'s setter function now takes a pure fn `state -> state`.
To get back the old behaviour of setting a value without
regard to its existing value, use `const`.
* `useReducer` now takes a (pure) `i -> s` initialiser
* `useEffect`, `useLayoutEffect` and their numbered variants no longer
take a redundant dummy unit value to delay computation.
### 0.3.1
Bug Fixes:
* `R.DOM.HTML` - more or less everything was broken since we moved out `createDOMElement`
### 0.3.0
Newly supported hooks and variants:
......@@ -109,6 +126,11 @@ Newly supported functions:
* `R.Hooks.nothing` - an empty cleanup function
* `R.Hooks.thenNothing` - make an effect function return an empty cleanup function
Changes:
* `R.createDOMElement` is now how you create DOM elements, we removed
the `R.IsComponent` instance for `String`
Bug Fixes:
* `R.React.render` was broken because the tests don't use it and
......@@ -146,6 +168,13 @@ Notable changes:
* Major refactor to use [ffi-simple](https://github.com/irresponsible/purescript-ffi-simple).
## Thanks
- [Nicolas Pouillard](https://github.com/np), for the discussions that
continue to shape the design of reactix.
- The rest of the [gargantext](https://gitlab.iscpif.fr/gargantext/purescript-gargantext)
team, for their enthusiasm both for reactix and for replacing thermite.
## Copyright and License
Copyright (c) 2018 James Laver
......
......@@ -4,7 +4,7 @@ module Reactix.Hooks
, useContext
, useRef
, useDebugValue, useDebugValue'
, HookEffect, nothing, thenNothing
, nothing, thenNothing
, useEffect, useEffect', useEffect1, useEffect1'
, useEffect2, useEffect2', useEffect3, useEffect3'
, useEffect4, useEffect4', useEffect5, useEffect5'
......@@ -34,21 +34,23 @@ import FFI.Simple ((...), (..), delay, args2, args3, args4, args5, setProperty)
import FFI.Simple.PseudoArray as Array
import DOM.Simple.Console
import Reactix.Utils (tuple, currySecond, hook)
import Reactix.React (Context, Ref, Hooks, react)
import Reactix.React (Context, Ref, Hooks, react, unsafeHooksEffect)
--- useState
-- | A state hook is a tuple of value and setter
type State state = Tuple state (state -> Effect Unit)
type State state = Tuple state ((state -> state) -> Effect Unit)
-- | Given an Effect function returning an initial value, returns a State
useState :: forall s. (Unit -> Effect s) -> Hooks (State s)
useState :: forall s. (Unit -> s) -> Hooks (State s)
useState s =
hook $ \_ ->
pure $ currySecond $ tuple $ react ... "useState" $ [ delay unit s ]
pure $ currySecond $ tuple $ react ... "useState" $ [ delay unit (pure <<< s) ]
useState' :: forall s. s -> Hooks (State s)
useState' s = useState $ \_ -> pure s
useState' s = useState $ const s
-- useReducer
-- | A reducer hook is a tuple of value and reducer-setter
......@@ -57,15 +59,15 @@ type Reducer state action = Tuple state (action -> Effect Unit)
-- | Given a reducer function from a state and action to a new state,
-- | an initialiser function and argument for the initialiser, returns
-- | a Reducer. Note args 2 and 3 are swapped in order from React.
useReducer :: forall s a i. (s -> a -> s) -> (i -> Effect s) -> i -> Hooks (Reducer s a)
useReducer :: forall s a i. (s -> a -> s) -> (i -> s) -> i -> Hooks (Reducer s a)
useReducer f i j =
hook $ \_ ->
pure $ currySecond $ tuple $ react ... "useReducer" $ args3 (mkFn2 f) j (mkEffectFn1 i)
pure $ currySecond $ tuple $ react ... "useReducer" $ args3 (mkFn2 f) j i
-- | Like `useReducer`, but takes an initial state instead of an
-- | initialiser function and argument
useReducer' :: forall s a. (s -> a -> s) -> s -> Hooks (Reducer s a)
useReducer' r = useReducer r pure
useReducer' r = useReducer r identity
-- useContext
......@@ -88,75 +90,68 @@ useDebugValue' v = hook $ \_ -> pure $ react ... "useDebugValue" $ [v]
-- useEffect
type HookEffect = Unit -> Effect (Unit -> Effect Unit)
-- | A cleanup handler that does nothing
nothing :: Unit -> Effect Unit
nothing _ = pure unit
nothing :: Effect Unit
nothing = pure unit
-- | Turns a simple effect function into an effect function that does
-- | nothing in cleanup after running
thenNothing :: forall a. (Unit -> Effect a) -> HookEffect
thenNothing e _ = e unit *> pure nothing
thenNothing :: forall a. Effect a -> Effect (Effect Unit)
thenNothing e = e *> pure nothing
-- | Given an Effect function which returns a cleanup Effect function,
-- | register an effect to be called after rendering
useEffect :: HookEffect -> Hooks Unit
useEffect e = hook $ \_ -> pure $ react ... "useEffect" $ [ wrapEffect e ]
useEffect :: Effect (Effect Unit) -> Hooks Unit
useEffect e = hook $ \_ -> pure $ react ... "useEffect" $ [e]
-- | Like useEffect, but with a memo value
useEffect1 :: forall a. a -> HookEffect -> Hooks Unit
useEffect1 :: forall a. a -> Effect (Effect Unit) -> Hooks Unit
useEffect1 a e = _useEffect e [a]
-- | Like useEffect, but with 2 memo values
useEffect2 :: forall a b. a -> b -> HookEffect -> Hooks Unit
useEffect2 :: forall a b. a -> b -> Effect (Effect Unit) -> Hooks Unit
useEffect2 a b e = _useEffect e $ args2 a b
-- | Like useEffect, but with 3 memo values
useEffect3 :: forall a b c. a -> b -> c -> HookEffect -> Hooks Unit
useEffect3 :: forall a b c. a -> b -> c -> Effect (Effect Unit) -> Hooks Unit
useEffect3 a b c e = _useEffect e $ args3 a b c
-- | Like useEffect, but with 4 memo values
useEffect4 :: forall a b c d. a -> b -> c -> d -> HookEffect -> Hooks Unit
useEffect4 :: forall a b c d. a -> b -> c -> d -> Effect (Effect Unit) -> Hooks Unit
useEffect4 a b c d e = _useEffect e $ args4 a b c d
-- | Like useEffect, but with 5 memo values
useEffect5 :: forall a b c d e. a -> b -> c -> d -> e -> HookEffect -> Hooks Unit
useEffect5 :: forall a b c d e. a -> b -> c -> d -> e -> Effect (Effect Unit) -> Hooks Unit
useEffect5 a b c d f e = _useEffect e $ args5 a b c d f
_useEffect :: forall a. HookEffect -> a -> Hooks Unit
_useEffect :: forall a. Effect (Effect Unit) -> a -> Hooks Unit
_useEffect e a =
hook $ \_ ->
pure $ react ... "useEffect" $
args2 (wrapEffect e) (Array.from a)
wrapEffect :: HookEffect -> Effect (Effect Unit)
wrapEffect f = delay unit $ \_ -> do
cleanup <- f unit
pure $ delay unit cleanup
args2 e (Array.from a)
-- | Like useEffect, but the provided Effect fn does not return a cleanup handler
useEffect' :: forall a. (Unit -> Effect a) -> Hooks Unit
useEffect' :: forall a. Effect a -> Hooks Unit
useEffect' = useEffect <<< thenNothing
-- | Like useEffect1, but the provided Effect fn does not return a cleanup handler
useEffect1' :: forall a b. a -> (Unit -> Effect b) -> Hooks Unit
useEffect1' :: forall a b. a -> Effect b -> Hooks Unit
useEffect1' a = useEffect1 a <<< thenNothing
-- | Like useEffect2, but the provided Effect fn does not return a cleanup handler
useEffect2' :: forall a b c. a -> b -> (Unit -> Effect c) -> Hooks Unit
useEffect2' :: forall a b c. a -> b -> Effect c -> Hooks Unit
useEffect2' a b = useEffect2 a b <<< thenNothing
-- | Like useEffect3, but the provided Effect fn does not return a cleanup handler
useEffect3' :: forall a b c d. a -> b -> c -> (Unit -> Effect d) -> Hooks Unit
useEffect3' :: forall a b c d. a -> b -> c -> Effect d -> Hooks Unit
useEffect3' a b c = useEffect3 a b c <<< thenNothing
-- | Like useEffect4, but the provided Effect fn does not return a cleanup handler
useEffect4' :: forall a b c d e. a -> b -> c -> d -> (Unit -> Effect e) -> Hooks Unit
useEffect4' :: forall a b c d e. a -> b -> c -> d -> Effect e -> Hooks Unit
useEffect4' a b c d = useEffect4 a b c d <<< thenNothing
-- | Like useEffect5, but the provided Effect fn does not return a cleanup handler
useEffect5' :: forall a b c d e f. a -> b -> c -> d -> e -> (Unit -> Effect f) -> Hooks Unit
useEffect5' :: forall a b c d e f. a -> b -> c -> d -> e -> Effect f -> Hooks Unit
useEffect5' a b c d e = useEffect5 a b c d e <<< thenNothing
-- useLayoutEffect
......@@ -164,62 +159,62 @@ useEffect5' a b c d e = useEffect5 a b c d e <<< thenNothing
-- | Given an Effect function which returns a cleanup Effect function,
-- | register an effect to be called in the same phase as
-- | `componentDidMount` and `componentDidUpdate` used to be.
useLayoutEffect :: HookEffect -> Hooks Unit
useLayoutEffect e = hook $ \_ -> pure $ react ... "useLayoutEffect" $ [ wrapEffect e ]
useLayoutEffect :: Effect (Effect Unit) -> Hooks Unit
useLayoutEffect e = hook $ \_ -> pure $ react ... "useLayoutEffect" $ [ e ]
-- | Like useLayoutEffect, but with a memo value
useLayoutEffect1 :: forall a. a -> HookEffect -> Hooks Unit
useLayoutEffect1 :: forall a. a -> Effect (Effect Unit) -> Hooks Unit
useLayoutEffect1 a e = _useLayoutEffect e [a]
-- | Like useLayoutEffect, but with 2 memo values
useLayoutEffect2 :: forall a b. a -> b -> HookEffect -> Hooks Unit
useLayoutEffect2 :: forall a b. a -> b -> Effect (Effect Unit) -> Hooks Unit
useLayoutEffect2 a b e = _useLayoutEffect e $ args2 a b
-- | Like useLayoutEffect, but with 3 memo values
useLayoutEffect3 :: forall a b c. a -> b -> c -> HookEffect -> Hooks Unit
useLayoutEffect3 :: forall a b c. a -> b -> c -> Effect (Effect Unit) -> Hooks Unit
useLayoutEffect3 a b c e = _useLayoutEffect e $ args3 a b c
-- | Like useLayoutEffect, but with 4 memo values
useLayoutEffect4 :: forall a b c d. a -> b -> c -> d -> HookEffect -> Hooks Unit
useLayoutEffect4 :: forall a b c d. a -> b -> c -> d -> Effect (Effect Unit) -> Hooks Unit
useLayoutEffect4 a b c d e = _useLayoutEffect e $ args4 a b c d
-- | Like useLayoutEffect, but with 5 memo values
useLayoutEffect5 :: forall a b c d e. a -> b -> c -> d -> e -> HookEffect -> Hooks Unit
useLayoutEffect5 :: forall a b c d e. a -> b -> c -> d -> e -> Effect (Effect Unit) -> Hooks Unit
useLayoutEffect5 a b c d f e = _useLayoutEffect e $ args5 a b c d f
-- | Like useLayoutEffect, but the provided Effect fn does not return a cleanup handler
useLayoutEffect' :: forall a. (Unit -> Effect a) -> Hooks Unit
useLayoutEffect' :: forall a. Effect a -> Hooks Unit
useLayoutEffect' = useLayoutEffect <<< thenNothing
-- | Like useLayoutEffect1, but the provided Effect fn does not return a cleanup handler
useLayoutEffect1' :: forall a b. a -> (Unit -> Effect b) -> Hooks Unit
useLayoutEffect1' :: forall a b. a -> Effect b -> Hooks Unit
useLayoutEffect1' a = useLayoutEffect1 a <<< thenNothing
-- | Like useLayoutEffect2, but the provided Effect fn does not return a cleanup handler
useLayoutEffect2' :: forall a b c. a -> b -> (Unit -> Effect c) -> Hooks Unit
useLayoutEffect2' :: forall a b c. a -> b -> Effect c -> Hooks Unit
useLayoutEffect2' a b = useLayoutEffect2 a b <<< thenNothing
-- | Like useLayoutEffect3, but the provided Effect fn does not return a cleanup handler
useLayoutEffect3' :: forall a b c d. a -> b -> c -> (Unit -> Effect d) -> Hooks Unit
useLayoutEffect3' :: forall a b c d. a -> b -> c -> Effect d -> Hooks Unit
useLayoutEffect3' a b c = useLayoutEffect3 a b c <<< thenNothing
-- | Like useLayoutEffect4, but the provided Effect fn does not return a cleanup handler
useLayoutEffect4' :: forall a b c d e. a -> b -> c -> d -> (Unit -> Effect e) -> Hooks Unit
useLayoutEffect4' :: forall a b c d e. a -> b -> c -> d -> Effect e -> Hooks Unit
useLayoutEffect4' a b c d = useLayoutEffect4 a b c d <<< thenNothing
-- | Like useLayoutEffect5, but the provided Effect fn does not return a cleanup handler
useLayoutEffect5' :: forall a b c d e f. a -> b -> c -> d -> e -> (Unit -> Effect f) -> Hooks Unit
useLayoutEffect5' :: forall a b c d e f. a -> b -> c -> d -> e -> Effect f -> Hooks Unit
useLayoutEffect5' a b c d f = useLayoutEffect5 a b c d f <<< thenNothing
_useLayoutEffect :: forall a. HookEffect -> a -> Hooks Unit
_useLayoutEffect :: forall a. Effect (Effect Unit) -> a -> Hooks Unit
_useLayoutEffect e a =
hook $ \_ ->
pure $ react ... "useLayoutEffect" $
args2 (wrapEffect e) (Array.from a)
args2 e (Array.from a)
-- useMemo
-- | Given a function to compure an expensive value, returns the value
-- | Given a function to compute an expensive value, returns the value
useMemo :: forall t. (Unit -> t) -> Hooks t
useMemo f = hook $ \_ -> pure $ react ... "useMemo" $ [ delay unit (\_ -> pure (f unit)) ]
......@@ -251,7 +246,7 @@ _useMemo f a =
-- useCallback
-- | Given a function to compure an expensive value, returns the value
-- | Given a function to compute an expensive value, returns the value
useCallback :: forall t. (Unit -> t) -> Hooks (Effect t)
useCallback f = hook $ \_ -> pure $ react ... "useCallback" $ [ delay unit (\_ -> pure (f unit)) ]
......@@ -283,39 +278,35 @@ _useCallback f a =
-- useImperativeHandle
useImperativeHandle
:: forall r r'
. Ref r -> (Unit -> Effect r') -> Hooks Unit
useImperativeHandle :: forall r r'. Ref r -> Effect r' -> Hooks Unit
useImperativeHandle r f =
hook $ \_ ->
pure $ react ... "useImperativeHandle" $ args2 r f
useImperativeHandle1
:: forall a r r'
. a -> Ref r -> (Unit -> Effect r') -> Hooks Unit
useImperativeHandle1 :: forall a r r'. a -> Ref r -> Effect r' -> Hooks Unit
useImperativeHandle1 a r f = _useImperativeHandle r f [a]
useImperativeHandle2
:: forall a b r r'
. a -> b -> Ref r -> (Unit -> Effect r') -> Hooks Unit
. a -> b -> Ref r -> Effect r' -> Hooks Unit
useImperativeHandle2 a b r f = _useImperativeHandle r f (args2 a b)
useImperativeHandle3
:: forall a b c r r'
. a -> b -> c -> Ref r -> (Unit -> Effect r') -> Hooks Unit
. a -> b -> c -> Ref r -> Effect r' -> Hooks Unit
useImperativeHandle3 a b c r f = _useImperativeHandle r f (args3 a b c)
useImperativeHandle4
:: forall a b c d r r'
. a -> b -> c -> d -> Ref r -> (Unit -> Effect r') -> Hooks Unit
. a -> b -> c -> d -> Ref r -> Effect r' -> Hooks Unit
useImperativeHandle4 a b c d r f = _useImperativeHandle r f (args4 a b c d)
useImperativeHandle5
:: forall a b c d e r r'
. a -> b -> c -> d -> e -> Ref r -> (Unit -> Effect r') -> Hooks Unit
. a -> b -> c -> d -> e -> Ref r -> Effect r' -> Hooks Unit
useImperativeHandle5 a b c d e r f = _useImperativeHandle r f (args5 a b c d e)
_useImperativeHandle :: forall r r' a. Ref r -> (Unit -> Effect r') -> a -> Hooks Unit
_useImperativeHandle :: forall r r' a. Ref r -> Effect r' -> a -> Hooks Unit
_useImperativeHandle r f a = hook $ \_ ->
pure $ react ... "useImperativeHandle" $
args3 r (delay unit f) (Array.from a)
args3 r f (Array.from a)
......@@ -21,6 +21,7 @@ import DOM.Simple as DOM
import DOM.Simple.Document as Document
import DOM.Simple.Element as Element
import DOM.Simple.Event as Event
import FFI.Simple (delay)
import Reactix as R
import Reactix.Test as RT
import Reactix.DOM.HTML ( button, div, i, text )
......@@ -50,9 +51,9 @@ counterCpt :: R.Component CounterProps
counterCpt = R.hooksComponent "Counter" cpt
where
cpt {count} _ = do
y /\ setY <- R.useState $ \_ -> pure count
y /\ setY <- R.useState' count
pure $ div { className: "counter" }
[ button { type: "button", onClick: onclick setY (y + 1) } [ text "++" ]
[ button { type: "button", onClick: onclick setY (_ + 1) } [ text "++" ]
, div {} [ text (show y) ] ]
onclick set to = mkEffectFn1 $ \e -> set to
......@@ -148,9 +149,9 @@ type EffectorProps = ( stateRef :: Ref.Ref EffectorState )
effectorCpt :: R.Component EffectorProps
effectorCpt = R.hooksComponent "Effector" cpt
where cpt {stateRef} _ = do
R.useEffect $ \_ -> do
R.useEffect $ do
Ref.write Initialised stateRef
pure $ \_ -> Ref.write Done stateRef
pure $ Ref.write Done stateRef
pure $ div {} []
-- TODO: test it's firing at the right time
......@@ -176,9 +177,9 @@ effectorTest =
layoutEffectorCpt :: R.Component EffectorProps
layoutEffectorCpt = R.hooksComponent "LayoutEffector" cpt
where cpt {stateRef} _ = do
R.useLayoutEffect $ \_ -> do
R.useLayoutEffect $ do
Ref.write Initialised stateRef
pure $ \_ -> Ref.write Done stateRef
pure $ delay unit $ \_ -> Ref.write Done stateRef
pure $ div {} []
-- TODO: test it's firing at the right time
......@@ -222,14 +223,14 @@ themeChooserCpt :: R.Component ThemeChooserProps
themeChooserCpt = R.hooksComponent "ThemeChooser" cpt
where
cpt props _ = do
theme /\ setTheme <- R.useState' $ Nothing
theme /\ setTheme <- R.useState' Nothing
ref <- R.useRef $ R.createContext Nothing
let context = R.readRef ref
pure $
div {}
[ button { type: "button", onClick: onclick setTheme Nothing } [ text "None" ]
, button { type: "button", onClick: onclick setTheme (Just Dark) } [ text "Dark" ]
, button { type: "button", onClick: onclick setTheme (Just Light) } [ text "Light" ]
[ button { type: "button", onClick: onclick setTheme (const Nothing) } [ text "None" ]
, button { type: "button", onClick: onclick setTheme (const $ Just Dark) } [ text "Dark" ]
, button { type: "button", onClick: onclick setTheme (const $ Just Light) } [ text "Light" ]
, R.provideContext context theme [ R.createElement themedCpt { theme: context } [] ] ]
onclick setTheme theme = mkEffectFn1 $ \_ -> setTheme theme
themeChooserTest :: Spec Unit
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment