From bc715d99001704618f1bacb76b115c072676f4d1 Mon Sep 17 00:00:00 2001 From: eric thul Date: Mon, 26 Nov 2018 08:38:32 -0500 Subject: [PATCH 1/5] Update example for hooks --- bower.json | 8 ++- package.json | 4 +- src/Example/TodoForm.purs | 70 ++++++++++++------------ src/Example/TodoItem.purs | 33 ++++++------ src/Example/TodoList.purs | 93 +++++++++++++++----------------- src/Main.purs | 70 ++++++++++++------------ yarn.lock | 108 +++++++++++--------------------------- 7 files changed, 169 insertions(+), 217 deletions(-) diff --git a/bower.json b/bower.json index a63a526..de10be1 100644 --- a/bower.json +++ b/bower.json @@ -8,7 +8,11 @@ "dependencies": { "purescript-react-dom": "^6.0.0", "purescript-console": "^4.0.0", - "purescript-react": "^6.0.0", - "purescript-web-html": "^1.0.0" + "purescript-react": "hooks", + "purescript-web-html": "^1.0.0", + "purescript-free": "^5.1.0" + }, + "resolutions": { + "purescript-react": "hooks" } } diff --git a/package.json b/package.json index 2e9e9f6..5436cfd 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "webpack-dev-server": "DEBUG=* webpack-dev-server --mode development --progress --inline --hot" }, "dependencies": { - "react": "^16.0.0", - "react-dom": "^16.0.0" + "react": "^16.7.0-alpha.2", + "react-dom": "^16.7.0-alpha.2" }, "devDependencies": { "pscid": "^2.0.2", diff --git a/src/Example/TodoForm.purs b/src/Example/TodoForm.purs index 5ac6bd1..f43ae08 100644 --- a/src/Example/TodoForm.purs +++ b/src/Example/TodoForm.purs @@ -5,8 +5,10 @@ import Prelude import Effect (Effect) import Data.Maybe (Maybe, maybe, isNothing) +import Data.Tuple (Tuple(..)) import React as React +import React.Hooks as Hooks import React.SyntheticEvent as Event import React.DOM as DOM import React.DOM.Props as Props @@ -21,45 +23,41 @@ type TodoFormProps , onAdd :: Todo -> Effect Unit } -todoFormClass :: React.ReactClass TodoFormProps -todoFormClass = React.component "TodoForm" component +todoForm :: TodoFormProps -> Effect React.ReactElement +todoForm + { todo + , onEdit + , onAdd + } = render <$> Hooks.useState "" where - component this = - pure { state: {} - , render: render <$> React.getProps this - } + render (Tuple value setState) = + DOM.form + [ Props.onSubmit onSubmit ] + [ DOM.input + [ Props._type "text" + , Props.value value + --, Props.onChange onChange + , Props.onChange \event -> Hooks.setState setState (unsafeCoerce event).target.value + ] + , DOM.button + [ Props._type "submit" + , Props.disabled isDisabled + ] + [ DOM.text "Add" ] + ] where - render - { todo - , onEdit - , onAdd - } = - DOM.form - [ Props.onSubmit onSubmit ] - [ DOM.input - [ Props._type "text" - , Props.value value - , Props.onChange onChange - ] - , DOM.button - [ Props._type "submit" - , Props.disabled isDisabled - ] - [ DOM.text "Add" ] - ] - where - value = maybe "" (\(Todo { text }) -> text) todo + value'' = maybe "" (\(Todo { text }) -> text) todo - isDisabled = isNothing todo + isDisabled = isNothing todo - onSubmit event = do - Event.preventDefault event + onSubmit event = do + Event.preventDefault event - maybe (pure unit) onAdd todo + maybe (pure unit) onAdd todo - onChange event = onEdit $ - maybe (Todo { text, status: TodoPending }) - (\(Todo todo_) -> Todo todo_ { text = text }) - todo - where - text = (unsafeCoerce event).target.value + onChange'' event = onEdit $ + maybe (Todo { text, status: TodoPending }) + (\(Todo todo_) -> Todo todo_ { text = text }) + todo + where + text = (unsafeCoerce event).target.value diff --git a/src/Example/TodoItem.purs b/src/Example/TodoItem.purs index 6d22aa6..16d5949 100644 --- a/src/Example/TodoItem.purs +++ b/src/Example/TodoItem.purs @@ -2,6 +2,8 @@ module Example.TodoItem where import Prelude +import Effect (Effect) + import React as React import React.DOM as DOM import React.DOM.Props as Props @@ -10,20 +12,19 @@ import Example.Types (Todo(..), TodoStatus(..)) type TodoItemProps = { todo :: Todo } -todoItemClass :: React.ReactClass TodoItemProps -todoItemClass = React.component "TodoItem" component +todoItem :: TodoItemProps -> Effect React.ReactElement +todoItem + { todo: Todo + { text + , status + } + } = pure $ + DOM.div + [ Props.style { textDecoration } ] + [ React.toElement text ] where - component this = - pure { state: {} - , render: render <$> React.getProps this - } - where - render { todo: Todo { text, status } } = - DOM.div - [ Props.style { textDecoration } ] - [ React.toElement text ] - where - textDecoration = - case status of - TodoDone -> "line-through" - _ -> "none" + textDecoration = + case status of + TodoDone -> "line-through" + _ -> "none" + diff --git a/src/Example/TodoList.purs b/src/Example/TodoList.purs index 60fb277..ff4ad20 100644 --- a/src/Example/TodoList.purs +++ b/src/Example/TodoList.purs @@ -11,8 +11,8 @@ import React as React import React.DOM as DOM import React.DOM.Props as Props -import Example.TodoForm (todoFormClass) -import Example.TodoItem (todoItemClass) +import Example.TodoForm (todoForm) +import Example.TodoItem (todoItem) import Example.Types (Todo(..), TodoStatus(..)) type TodoListProps @@ -24,55 +24,48 @@ type TodoListProps , onClear :: Todo -> Effect Unit } -todoListClass :: React.ReactClass TodoListProps -todoListClass = React.component "TodoList" component - where - component this = - pure { state: {} - , render: render <$> React.getProps this - } - where - render - { todos - , todo - , onAdd - , onEdit - , onDone - , onClear - } = - DOM.div +todoList :: TodoListProps -> Effect React.ReactElement +todoList + { todos + , todo + , onAdd + , onEdit + , onDone + , onClear + } = pure $ + DOM.div + [ ] + [ React.createElementHooks todoForm + { todo + , onEdit + , onAdd + } + , DOM.ol [ ] - [ React.createLeafElement todoFormClass - { todo - , onEdit - , onAdd - } - , DOM.ol - [ ] - (renderItem <$> todos') - ] - where - todos' = filter (\(Todo { status }) -> TodoCleared /= status) todos + (renderItem <$> todos') + ] + where + todos' = filter (\(Todo { status }) -> TodoCleared /= status) todos - renderItem todo' @ Todo { status } = - DOM.li - [ ] - [ React.createLeafElement todoItemClass { todo: todo' } - , DOM.button - [ Props._type "button" - , Props.onClick onClick - ] - [ DOM.text text ] + renderItem todo' @ Todo { status } = + DOM.li + [ ] + [ React.createElementHooks todoItem { todo: todo' } + , DOM.button + [ Props._type "button" + , Props.onClick onClick ] - where - text = - case status of - TodoPending -> "Done" - TodoDone -> "Clear" - _ -> "" + [ DOM.text text ] + ] + where + text = + case status of + TodoPending -> "Done" + TodoDone -> "Clear" + _ -> "" - onClick event = - case status of - TodoPending -> onDone todo' - TodoDone -> onClear todo' - _ -> pure unit + onClick event = + case status of + TodoPending -> onDone todo' + TodoDone -> onClear todo' + _ -> pure unit diff --git a/src/Main.purs b/src/Main.purs index 4ff1e4e..809573e 100644 --- a/src/Main.purs +++ b/src/Main.purs @@ -6,6 +6,7 @@ import Effect (Effect) import Data.Array (snoc, modifyAt, elemIndex) import Data.Maybe (Maybe(..), fromJust, fromMaybe) +import Data.Tuple (Tuple(..)) import Web.HTML.HTMLDocument (toNonElementParentNode) as DOM import Web.DOM.NonElementParentNode (getElementById) as DOM @@ -15,9 +16,10 @@ import Web.HTML.Window (document) as DOM import Partial.Unsafe (unsafePartial) import React as React +import React.Hooks as Hooks import ReactDOM as ReactDOM -import Example.TodoList (todoListClass) +import Example.TodoList (todoList) import Example.Types (Todo(..), TodoStatus(..)) main :: Effect Unit @@ -34,46 +36,44 @@ main = void $ do let element' = unsafePartial (fromJust element) - ReactDOM.render (React.createLeafElement mainClass { }) element' + ReactDOM.render (React.createElementHooks wrapper { }) element' -mainClass :: React.ReactClass { } -mainClass = React.component "Main" component +wrapper :: { } -> Effect React.ReactElement +wrapper _ = render <$> Hooks.useState initialState where - component this = - pure { state: - { todo: Nothing - , todos: [] - } - , render: render <$> React.getState this - } - where - render + initialState = + { todo: Nothing + , todos: [] + } + + render + (Tuple { todo , todos - } = - React.createLeafElement todoListClass - { todos - , todo - - , onAdd: \todo' -> React.modifyState this \a -> - a { todo = Nothing - , todos = snoc a.todos todo' - } + } setState) = + React.createElementHooks todoList + { todos + , todo + + , onAdd: \todo' -> Hooks.modifyState setState \a -> + a { todo = Nothing + , todos = snoc a.todos todo' + } - , onEdit: \todo' -> React.modifyState this - _ { todo = Just todo' - } + , onEdit: \todo' -> Hooks.modifyState setState + _ { todo = Just todo' + } - , onDone: \todo' -> React.modifyState this \a -> - a { todos = setStatus a.todos todo' TodoDone - } + , onDone: \todo' -> Hooks.modifyState setState \a -> + a { todos = setStatus a.todos todo' TodoDone + } - , onClear : \todo' -> React.modifyState this \a -> - a { todos = setStatus a.todos todo' TodoCleared - } - } + , onClear : \todo' -> Hooks.modifyState setState \a -> + a { todos = setStatus a.todos todo' TodoCleared + } + } - setStatus todos todo status = fromMaybe todos $ do - i <- elemIndex todo todos + setStatus todos todo status = fromMaybe todos $ do + i <- elemIndex todo todos - modifyAt i (\(Todo a) -> Todo a { status = status }) todos + modifyAt i (\(Todo a) -> Todo a { status = status }) todos diff --git a/yarn.lock b/yarn.lock index 2fdb4d0..fbb634f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -326,10 +326,6 @@ arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -1005,7 +1001,11 @@ bl@^1.0.0: readable-stream "^2.3.5" safe-buffer "^5.1.1" -bluebird@^3.3.5, bluebird@^3.5.1: +bluebird@^3.3.5: + version "3.5.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.2.tgz#1be0908e054a751754549c270489c1505d4ab15a" + +bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -1557,10 +1557,6 @@ copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" -core-js@^1.0.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0: version "2.5.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" @@ -1932,12 +1928,6 @@ encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" -encoding@^0.1.11: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" - dependencies: - iconv-lite "~0.4.13" - end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" @@ -2242,18 +2232,6 @@ faye-websocket@~0.11.0: dependencies: websocket-driver ">=0.5.1" -fbjs@^0.8.16: - version "0.8.16" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" - dependencies: - core-js "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.9" - feint@1.0.2, feint@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/feint/-/feint-1.0.2.tgz#f4ac51d6ed7db2cc5a0ba6913f84d43f72dd680b" @@ -2828,7 +2806,7 @@ iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" -iconv-lite@^0.4.17, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@^0.4.17, iconv-lite@^0.4.4: version "0.4.23" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" dependencies: @@ -3216,7 +3194,7 @@ is-scoped@^1.0.0: dependencies: scoped-regex "^1.0.0" -is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -3258,13 +3236,6 @@ isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" -isomorphic-fetch@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" - dependencies: - node-fetch "^1.0.1" - whatwg-fetch ">=0.10.0" - istextorbinary@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.2.1.tgz#a5231a08ef6dd22b268d0895084cf8d58b5bec53" @@ -3354,7 +3325,7 @@ json3@^3.3.2: json5@^0.5.0, json5@^0.5.1: version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + resolved "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" junk@^2.1.0: version "2.1.0" @@ -3937,13 +3908,6 @@ node-dir@0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.8.tgz#55fb8deb699070707fb67f91a460f0448294c77d" -node-fetch@^1.0.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - node-forge@0.7.5: version "0.7.5" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" @@ -4385,7 +4349,7 @@ pbkdf2@^3.0.3: pify@^2.0.0, pify@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + resolved "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" pify@^3.0.0: version "3.0.0" @@ -4486,17 +4450,10 @@ promise-retry@^1.1.0: err-code "^1.0.0" retry "^0.10.0" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" +prop-types@^15.6.2: + version "15.6.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: - asap "~2.0.3" - -prop-types@^15.6.0: - version "15.6.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" - dependencies: - fbjs "^0.8.16" loose-envify "^1.3.1" object-assign "^4.1.1" @@ -4583,8 +4540,8 @@ purescript@^0.12.0: install-purescript-cli "^0.4.0 || ^0.3.0" purs-loader@^3.0.0: - version "3.1.4" - resolved "https://registry.yarnpkg.com/purs-loader/-/purs-loader-3.1.4.tgz#92a4326e748bdf7aff9d69f4a10feb8466e148a6" + version "3.2.0" + resolved "https://registry.yarnpkg.com/purs-loader/-/purs-loader-3.2.0.tgz#90dea335976fb87199f721969eaee95ad6810721" dependencies: bluebird "^3.3.5" chalk "^1.1.3" @@ -4670,23 +4627,23 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@^16.0.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.0.tgz#099f067dd5827ce36a29eaf9a6cdc7cbf6216b1e" +react-dom@^16.7.0-alpha.2: + version "16.7.0-alpha.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0-alpha.2.tgz#16632880ed43676315991d8b412cce6975a30282" dependencies: - fbjs "^0.8.16" loose-envify "^1.1.0" object-assign "^4.1.1" - prop-types "^15.6.0" + prop-types "^15.6.2" + scheduler "^0.12.0-alpha.2" -react@^16.0.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.4.0.tgz#402c2db83335336fba1962c08b98c6272617d585" +react@^16.7.0-alpha.2: + version "16.7.0-alpha.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.7.0-alpha.2.tgz#924f2ae843a46ea82d104a8def7a599fbf2c78ce" dependencies: - fbjs "^0.8.16" loose-envify "^1.1.0" object-assign "^4.1.1" - prop-types "^15.6.0" + prop-types "^15.6.2" + scheduler "^0.12.0-alpha.2" read-chunk@^2.1.0: version "2.1.0" @@ -5014,6 +4971,13 @@ sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" +scheduler@^0.12.0-alpha.2: + version "0.12.0-alpha.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0-alpha.2.tgz#2a8bc8dc6ecdb75fa6480ceeedc1f187c9539970" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^0.4.4, schema-utils@^0.4.5: version "0.4.5" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" @@ -5108,7 +5072,7 @@ set-value@^2.0.0: is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@^1.0.4, setimmediate@^1.0.5: +setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -5642,10 +5606,6 @@ typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -ua-parser-js@^0.7.9: - version "0.7.18" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" - uglify-es@^3.3.4: version "3.3.9" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" @@ -5994,10 +5954,6 @@ websocket-extensions@>=0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" -whatwg-fetch@>=0.10.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" - which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" From afd619c293cd6317bce6cd8f0efd2c062d3502de Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 1 Dec 2018 10:22:20 -0500 Subject: [PATCH 2/5] Update example --- src/Example/TodoForm.purs | 20 +++++++++----------- src/Example/TodoItem.purs | 5 ++--- src/Example/TodoList.purs | 7 ++++--- src/Main.purs | 19 ++++++++++--------- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/Example/TodoForm.purs b/src/Example/TodoForm.purs index f43ae08..04da3c5 100644 --- a/src/Example/TodoForm.purs +++ b/src/Example/TodoForm.purs @@ -2,13 +2,12 @@ module Example.TodoForm where import Prelude -import Effect (Effect) - import Data.Maybe (Maybe, maybe, isNothing) -import Data.Tuple (Tuple(..)) + +import Effect (Effect) import React as React -import React.Hooks as Hooks +import React.Hook (Hook) import React.SyntheticEvent as Event import React.DOM as DOM import React.DOM.Props as Props @@ -23,21 +22,20 @@ type TodoFormProps , onAdd :: Todo -> Effect Unit } -todoForm :: TodoFormProps -> Effect React.ReactElement +todoForm :: TodoFormProps -> Hook React.ReactElement todoForm { todo , onEdit , onAdd - } = render <$> Hooks.useState "" + } = pure render where - render (Tuple value setState) = + render = DOM.form [ Props.onSubmit onSubmit ] [ DOM.input [ Props._type "text" , Props.value value - --, Props.onChange onChange - , Props.onChange \event -> Hooks.setState setState (unsafeCoerce event).target.value + , Props.onChange onChange ] , DOM.button [ Props._type "submit" @@ -46,7 +44,7 @@ todoForm [ DOM.text "Add" ] ] where - value'' = maybe "" (\(Todo { text }) -> text) todo + value = maybe "" (\(Todo { text }) -> text) todo isDisabled = isNothing todo @@ -55,7 +53,7 @@ todoForm maybe (pure unit) onAdd todo - onChange'' event = onEdit $ + onChange event = onEdit $ maybe (Todo { text, status: TodoPending }) (\(Todo todo_) -> Todo todo_ { text = text }) todo diff --git a/src/Example/TodoItem.purs b/src/Example/TodoItem.purs index 16d5949..56e2dc3 100644 --- a/src/Example/TodoItem.purs +++ b/src/Example/TodoItem.purs @@ -2,9 +2,8 @@ module Example.TodoItem where import Prelude -import Effect (Effect) - import React as React +import React.Hook (Hook) import React.DOM as DOM import React.DOM.Props as Props @@ -12,7 +11,7 @@ import Example.Types (Todo(..), TodoStatus(..)) type TodoItemProps = { todo :: Todo } -todoItem :: TodoItemProps -> Effect React.ReactElement +todoItem :: TodoItemProps -> Hook React.ReactElement todoItem { todo: Todo { text diff --git a/src/Example/TodoList.purs b/src/Example/TodoList.purs index ff4ad20..4b4d37c 100644 --- a/src/Example/TodoList.purs +++ b/src/Example/TodoList.purs @@ -8,6 +8,7 @@ import Data.Array (filter) import Data.Maybe (Maybe) import React as React +import React.Hook (Hook) import React.DOM as DOM import React.DOM.Props as Props @@ -24,7 +25,7 @@ type TodoListProps , onClear :: Todo -> Effect Unit } -todoList :: TodoListProps -> Effect React.ReactElement +todoList :: TodoListProps -> Hook React.ReactElement todoList { todos , todo @@ -35,7 +36,7 @@ todoList } = pure $ DOM.div [ ] - [ React.createElementHooks todoForm + [ React.createElementHook todoForm { todo , onEdit , onAdd @@ -50,7 +51,7 @@ todoList renderItem todo' @ Todo { status } = DOM.li [ ] - [ React.createElementHooks todoItem { todo: todo' } + [ React.createElementHook todoItem { todo: todo' } , DOM.button [ Props._type "button" , Props.onClick onClick diff --git a/src/Main.purs b/src/Main.purs index 809573e..2ef1d98 100644 --- a/src/Main.purs +++ b/src/Main.purs @@ -16,7 +16,8 @@ import Web.HTML.Window (document) as DOM import Partial.Unsafe (unsafePartial) import React as React -import React.Hooks as Hooks +import React.Hook (Hook) +import React.Hook as Hook import ReactDOM as ReactDOM import Example.TodoList (todoList) @@ -36,10 +37,10 @@ main = void $ do let element' = unsafePartial (fromJust element) - ReactDOM.render (React.createElementHooks wrapper { }) element' + ReactDOM.render (React.createElementHook wrapper { }) element' -wrapper :: { } -> Effect React.ReactElement -wrapper _ = render <$> Hooks.useState initialState +wrapper :: { } -> Hook React.ReactElement +wrapper _ = render <$> Hook.useState initialState where initialState = { todo: Nothing @@ -51,24 +52,24 @@ wrapper _ = render <$> Hooks.useState initialState { todo , todos } setState) = - React.createElementHooks todoList + React.createElementHook todoList { todos , todo - , onAdd: \todo' -> Hooks.modifyState setState \a -> + , onAdd: \todo' -> Hook.modifyState setState \a -> a { todo = Nothing , todos = snoc a.todos todo' } - , onEdit: \todo' -> Hooks.modifyState setState + , onEdit: \todo' -> Hook.modifyState setState _ { todo = Just todo' } - , onDone: \todo' -> Hooks.modifyState setState \a -> + , onDone: \todo' -> Hook.modifyState setState \a -> a { todos = setStatus a.todos todo' TodoDone } - , onClear : \todo' -> Hooks.modifyState setState \a -> + , onClear : \todo' -> Hook.modifyState setState \a -> a { todos = setStatus a.todos todo' TodoCleared } } From 3c292238e591d89f6fdeaef2537398857f352d68 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sun, 2 Dec 2018 09:46:00 -0500 Subject: [PATCH 3/5] Hook usage update --- src/Example.purs | 75 +++++++++++++++++++++++++++++++++++++++ src/Example/TodoList.purs | 4 +-- src/Main.purs | 51 ++------------------------ 3 files changed, 80 insertions(+), 50 deletions(-) create mode 100644 src/Example.purs diff --git a/src/Example.purs b/src/Example.purs new file mode 100644 index 0000000..7b76b7d --- /dev/null +++ b/src/Example.purs @@ -0,0 +1,75 @@ +module Example where + +import Prelude + +import Data.Array (snoc, modifyAt, elemIndex) +import Data.Maybe (Maybe(..), fromMaybe) +import Data.Tuple (Tuple(..)) + +import React as React +import React.Hook (Hook) +import React.Hook as Hook + +import Example.TodoList (todoList) +import Example.Types (Todo(..), TodoStatus(..)) + +data Action + = Add Todo + | Edit Todo + | Done Todo + | Clear Todo + +example :: { } -> Hook React.ReactElement +example _ = + React.createHookLeafElement todoList <$> hook + where + hook = do + Tuple state dispatch <- Hook.useReducer reducer initialState + + let + inputs = Just [ Hook.hookInput dispatch ] + + onAdd <- Hook.useCallback (Hook.dispatch dispatch <<< Add) inputs + + onEdit <- Hook.useCallback (Hook.dispatch dispatch <<< Edit) inputs + + onDone <- Hook.useCallback (Hook.dispatch dispatch <<< Done) inputs + + onClear <- Hook.useCallback (Hook.dispatch dispatch <<< Clear) inputs + + pure { todo: state.todo + , todos: state.todos + , onAdd + , onEdit + , onDone + , onClear + } + where + initialState = + { todo: Nothing + , todos: [ ] + } + + reducer state = + case _ of + Add todo -> state + { todo = Nothing + , todos = snoc state.todos todo + } + + Edit todo -> state + { todo = Just todo + } + + Done todo -> state + { todos = setStatus todo TodoDone + } + + Clear todo -> state + { todos = setStatus todo TodoCleared + } + where + setStatus todo status = fromMaybe state.todos $ do + i <- elemIndex todo state.todos + + modifyAt i (\(Todo a) -> Todo a { status = status }) state.todos diff --git a/src/Example/TodoList.purs b/src/Example/TodoList.purs index 4b4d37c..7a899f0 100644 --- a/src/Example/TodoList.purs +++ b/src/Example/TodoList.purs @@ -36,7 +36,7 @@ todoList } = pure $ DOM.div [ ] - [ React.createElementHook todoForm + [ React.createHookLeafElement todoForm { todo , onEdit , onAdd @@ -51,7 +51,7 @@ todoList renderItem todo' @ Todo { status } = DOM.li [ ] - [ React.createElementHook todoItem { todo: todo' } + [ React.createHookLeafElement todoItem { todo: todo' } , DOM.button [ Props._type "button" , Props.onClick onClick diff --git a/src/Main.purs b/src/Main.purs index 2ef1d98..433dd44 100644 --- a/src/Main.purs +++ b/src/Main.purs @@ -4,9 +4,7 @@ import Prelude import Effect (Effect) -import Data.Array (snoc, modifyAt, elemIndex) -import Data.Maybe (Maybe(..), fromJust, fromMaybe) -import Data.Tuple (Tuple(..)) +import Data.Maybe (fromJust) import Web.HTML.HTMLDocument (toNonElementParentNode) as DOM import Web.DOM.NonElementParentNode (getElementById) as DOM @@ -16,12 +14,9 @@ import Web.HTML.Window (document) as DOM import Partial.Unsafe (unsafePartial) import React as React -import React.Hook (Hook) -import React.Hook as Hook import ReactDOM as ReactDOM -import Example.TodoList (todoList) -import Example.Types (Todo(..), TodoStatus(..)) +import Example (example) main :: Effect Unit main = void $ do @@ -37,44 +32,4 @@ main = void $ do let element' = unsafePartial (fromJust element) - ReactDOM.render (React.createElementHook wrapper { }) element' - -wrapper :: { } -> Hook React.ReactElement -wrapper _ = render <$> Hook.useState initialState - where - initialState = - { todo: Nothing - , todos: [] - } - - render - (Tuple - { todo - , todos - } setState) = - React.createElementHook todoList - { todos - , todo - - , onAdd: \todo' -> Hook.modifyState setState \a -> - a { todo = Nothing - , todos = snoc a.todos todo' - } - - , onEdit: \todo' -> Hook.modifyState setState - _ { todo = Just todo' - } - - , onDone: \todo' -> Hook.modifyState setState \a -> - a { todos = setStatus a.todos todo' TodoDone - } - - , onClear : \todo' -> Hook.modifyState setState \a -> - a { todos = setStatus a.todos todo' TodoCleared - } - } - - setStatus todos todo status = fromMaybe todos $ do - i <- elemIndex todo todos - - modifyAt i (\(Todo a) -> Todo a { status = status }) todos + ReactDOM.render (React.createHookLeafElement example { }) element' From ff530bb097cf2d0c4da5fe18bd0aa9d966237823 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 8 Dec 2018 17:24:49 -0500 Subject: [PATCH 4/5] Add context and ref examples --- src/Example/TodoContext.purs | 11 +++++++++++ src/Example/TodoForm.js | 7 +++++++ src/Example/TodoForm.purs | 33 ++++++++++++++++++++++++++++++--- src/Example/TodoItem.purs | 15 ++++++++++++--- src/Example/TodoList.purs | 11 +++++++++-- 5 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 src/Example/TodoContext.purs create mode 100644 src/Example/TodoForm.js diff --git a/src/Example/TodoContext.purs b/src/Example/TodoContext.purs new file mode 100644 index 0000000..c394120 --- /dev/null +++ b/src/Example/TodoContext.purs @@ -0,0 +1,11 @@ +module Example.TodoContext where + +import Data.Maybe (Maybe(..)) + +import React.Context (Context) +import React.Context as Context + +type TodoContext = Context { backgroundColor :: String } + +todoContext :: TodoContext +todoContext = Context.createContext { backgroundColor: "red" } Nothing diff --git a/src/Example/TodoForm.js b/src/Example/TodoForm.js new file mode 100644 index 0000000..67d65de --- /dev/null +++ b/src/Example/TodoForm.js @@ -0,0 +1,7 @@ +'use strict'; + +exports.blur = function blur(ref) { + return function () { + return ref.blur(); + }; +} diff --git a/src/Example/TodoForm.purs b/src/Example/TodoForm.purs index 04da3c5..c510c71 100644 --- a/src/Example/TodoForm.purs +++ b/src/Example/TodoForm.purs @@ -2,18 +2,22 @@ module Example.TodoForm where import Prelude -import Data.Maybe (Maybe, maybe, isNothing) +import Data.Maybe (Maybe(..), maybe, isNothing) import Effect (Effect) import React as React import React.Hook (Hook) +import React.Hook as Hook +import React.Ref (Ref, DOMRef) +import React.Ref as Ref import React.SyntheticEvent as Event import React.DOM as DOM import React.DOM.Props as Props import Unsafe.Coerce (unsafeCoerce) +import Example.TodoContext as TodoContext import Example.Types (Todo(..), TodoStatus(..)) type TodoFormProps @@ -27,15 +31,30 @@ todoForm { todo , onEdit , onAdd - } = pure render + } = + render <$> hook where - render = + hook = do + context <- Hook.useContext TodoContext.todoContext + + ref <- Hook.useRef Nothing + + pure { ref, context } + + render + { ref + , context: + { backgroundColor + } + } = DOM.form [ Props.onSubmit onSubmit ] [ DOM.input [ Props._type "text" , Props.value value + , Props.ref ref , Props.onChange onChange + , Props.style { backgroundColor } ] , DOM.button [ Props._type "submit" @@ -53,9 +72,17 @@ todoForm maybe (pure unit) onAdd todo + domRef <- Ref.getRef ref + + maybe (pure unit) blur domRef + + pure unit + onChange event = onEdit $ maybe (Todo { text, status: TodoPending }) (\(Todo todo_) -> Todo todo_ { text = text }) todo where text = (unsafeCoerce event).target.value + +foreign import blur :: DOMRef -> Effect Unit diff --git a/src/Example/TodoItem.purs b/src/Example/TodoItem.purs index 56e2dc3..3f7b70f 100644 --- a/src/Example/TodoItem.purs +++ b/src/Example/TodoItem.purs @@ -3,10 +3,12 @@ module Example.TodoItem where import Prelude import React as React +import React.Context as Context import React.Hook (Hook) import React.DOM as DOM import React.DOM.Props as Props +import Example.TodoContext as TodoContext import Example.Types (Todo(..), TodoStatus(..)) type TodoItemProps = { todo :: Todo } @@ -18,10 +20,17 @@ todoItem , status } } = pure $ - DOM.div - [ Props.style { textDecoration } ] - [ React.toElement text ] + React.createRenderPropsElement consumer { } $ \{ backgroundColor } -> + DOM.div + [ Props.style + { textDecoration + , backgroundColor + } + ] + [ React.toElement text ] where + consumer = Context.getConsumer TodoContext.todoContext + textDecoration = case status of TodoDone -> "line-through" diff --git a/src/Example/TodoList.purs b/src/Example/TodoList.purs index 7a899f0..f84ef0c 100644 --- a/src/Example/TodoList.purs +++ b/src/Example/TodoList.purs @@ -8,10 +8,12 @@ import Data.Array (filter) import Data.Maybe (Maybe) import React as React +import React.Context as Context import React.Hook (Hook) import React.DOM as DOM import React.DOM.Props as Props +import Example.TodoContext as TodoContext import Example.TodoForm (todoForm) import Example.TodoItem (todoItem) import Example.Types (Todo(..), TodoStatus(..)) @@ -34,8 +36,11 @@ todoList , onDone , onClear } = pure $ - DOM.div - [ ] + React.createElement provider + { value: + { backgroundColor: "lightblue" + } + } [ React.createHookLeafElement todoForm { todo , onEdit @@ -46,6 +51,8 @@ todoList (renderItem <$> todos') ] where + provider = Context.getProvider TodoContext.todoContext + todos' = filter (\(Todo { status }) -> TodoCleared /= status) todos renderItem todo' @ Todo { status } = From b97f77877d11c79c47e37b4b1e7a97868acad536 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 8 Dec 2018 17:27:40 -0500 Subject: [PATCH 5/5] Use HTMLElement --- src/Example/TodoForm.js | 7 ------- src/Example/TodoForm.purs | 7 +++---- 2 files changed, 3 insertions(+), 11 deletions(-) delete mode 100644 src/Example/TodoForm.js diff --git a/src/Example/TodoForm.js b/src/Example/TodoForm.js deleted file mode 100644 index 67d65de..0000000 --- a/src/Example/TodoForm.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -exports.blur = function blur(ref) { - return function () { - return ref.blur(); - }; -} diff --git a/src/Example/TodoForm.purs b/src/Example/TodoForm.purs index c510c71..db81e40 100644 --- a/src/Example/TodoForm.purs +++ b/src/Example/TodoForm.purs @@ -9,12 +9,13 @@ import Effect (Effect) import React as React import React.Hook (Hook) import React.Hook as Hook -import React.Ref (Ref, DOMRef) import React.Ref as Ref import React.SyntheticEvent as Event import React.DOM as DOM import React.DOM.Props as Props +import Web.HTML.HTMLElement as HTML + import Unsafe.Coerce (unsafeCoerce) import Example.TodoContext as TodoContext @@ -74,7 +75,7 @@ todoForm domRef <- Ref.getRef ref - maybe (pure unit) blur domRef + maybe (pure unit) (HTML.blur <<< unsafeCoerce) domRef pure unit @@ -84,5 +85,3 @@ todoForm todo where text = (unsafeCoerce event).target.value - -foreign import blur :: DOMRef -> Effect Unit