From 5e6a1551fe86ef8ecc5b7ff56fd82d5c4ccf521a Mon Sep 17 00:00:00 2001 From: Marcin Szamotulski Date: Sat, 8 Jul 2017 11:08:54 +0200 Subject: [PATCH 1/6] React-16: polymorphic Render type --- package.json | 2 +- src/React.purs | 33 +++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 1e1b0fc..a9d0e9b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "purescript-react", "files": [], "peerDependencies": { - "react": "^15.6.1", + "react": "^16.0.0-alpha.13", "create-react-class": "^15.6.0" } } diff --git a/src/React.purs b/src/React.purs index d3c9d63..bcd9ee9 100644 --- a/src/React.purs +++ b/src/React.purs @@ -1,7 +1,8 @@ -- | This module defines foreign types and functions which wrap React's functionality. module React - ( ReactElement + ( class ReactRender + , ReactElement , ReactComponent , ReactThis , TagName @@ -147,15 +148,21 @@ type EventHandlerContext eff props state result = | eff ) result +class ReactRender a + +instance arrayReactRender :: ReactRender (Array ReactElement) + +instance reactElementReactRender :: ReactRender ReactElement + -- | A render function. -type Render props state eff = +type Render props state render eff = ReactThis props state -> Eff ( props :: ReactProps , refs :: ReactRefs Disallowed , state :: ReactState ReadOnly | eff - ) ReactElement + ) render -- | A get initial state function. type GetInitialState props state eff = @@ -245,8 +252,8 @@ type ComponentWillUnmount props state eff = ) Unit -- | A specification of a component. -type ReactSpec props state eff = - { render :: Render props state eff +type ReactSpec props state render eff = + { render :: Render props state render eff , displayName :: String , getInitialState :: GetInitialState props state eff , componentWillMount :: ComponentWillMount props state eff @@ -259,15 +266,17 @@ type ReactSpec props state eff = } -- | Create a component specification with a provided state. -spec :: forall props state eff. - state -> Render props state eff -> ReactSpec props state eff +spec :: forall props state render eff. + ReactRender render => + state -> Render props state render eff -> ReactSpec props state render eff spec state = spec' \_ -> pure state -- | Create a component specification with a get initial state function. -spec' :: forall props state eff. +spec' :: forall props state render eff. + ReactRender render => GetInitialState props state eff -> - Render props state eff -> - ReactSpec props state eff + Render props state render eff -> + ReactSpec props state render eff spec' getInitialState renderFn = { render: renderFn , displayName: "" @@ -320,8 +329,8 @@ foreign import transformState :: forall props state eff. Eff (state :: ReactState ReadWrite | eff) Unit -- | Create a React class from a specification. -foreign import createClass :: forall props state eff. - ReactSpec props state eff -> ReactClass props +foreign import createClass :: forall props state render eff. + ReactSpec props state render eff -> ReactClass props -- | Create a stateless React class. createClassStateless :: forall props. From ccf400df6a03fbcb6a2e51a7ba29238bf54d9d5e Mon Sep 17 00:00:00 2001 From: Marcin Szamotulski Date: Sat, 5 Aug 2017 11:54:34 +0200 Subject: [PATCH 2/6] stateless components --- src/React.purs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/React.purs b/src/React.purs index bcd9ee9..9381446 100644 --- a/src/React.purs +++ b/src/React.purs @@ -333,13 +333,15 @@ foreign import createClass :: forall props state render eff. ReactSpec props state render eff -> ReactClass props -- | Create a stateless React class. -createClassStateless :: forall props. - (props -> ReactElement) -> ReactClass props +createClassStateless :: forall props render. + ReactRender render => + (props -> render) -> ReactClass props createClassStateless = unsafeCoerce -- | Create a stateless React class with children access. -createClassStateless' :: forall props. - (props -> Array ReactElement -> ReactElement) -> ReactClass props +createClassStateless' :: forall props render. + ReactRender render => + (props -> Array ReactElement -> render) -> ReactClass props createClassStateless' k = createClassStateless \props -> k props (childrenToArray (unsafeCoerce props).children) From 540624d318a71861f5a930c885464b97da17fcb0 Mon Sep 17 00:00:00 2001 From: Marcin Szamotulski Date: Sat, 30 Sep 2017 14:47:45 +0200 Subject: [PATCH 3/6] Added ReactRender String instance. --- src/React.purs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/React.purs b/src/React.purs index 9381446..f789634 100644 --- a/src/React.purs +++ b/src/React.purs @@ -154,6 +154,8 @@ instance arrayReactRender :: ReactRender (Array ReactElement) instance reactElementReactRender :: ReactRender ReactElement +instance stringReactRender :: ReactRender String + -- | A render function. type Render props state render eff = ReactThis props state -> From cf2ce4c4cc217444ef1c951e0d8877caf9456242 Mon Sep 17 00:00:00 2001 From: Marcin Szamotulski Date: Sat, 30 Sep 2017 16:06:08 +0200 Subject: [PATCH 4/6] ReactRender: int & number Also add `React.DOM.int` and `React.DOM.number` functions. --- src/React.purs | 4 ++++ src/React/DOM.purs | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/React.purs b/src/React.purs index f789634..67a382c 100644 --- a/src/React.purs +++ b/src/React.purs @@ -156,6 +156,10 @@ instance reactElementReactRender :: ReactRender ReactElement instance stringReactRender :: ReactRender String +instance intReactRender :: ReactRender Int + +instance numberReactRender :: ReactRender Number + -- | A render function. type Render props state render eff = ReactThis props state -> diff --git a/src/React/DOM.purs b/src/React/DOM.purs index fbb5c18..079cce6 100644 --- a/src/React/DOM.purs +++ b/src/React/DOM.purs @@ -19,6 +19,12 @@ mkDOM dynamic tag props = createElement tag (unsafeFromPropsArray props) text :: String -> ReactElement text = unsafeCoerce +int :: Int -> ReactElement +int = unsafeCoerce + +number :: Number -> ReactElement +number = unsafeCoerce + a :: Array Props -> Array ReactElement -> ReactElement a = mkDOM (IsDynamic false) "a" From e586e30e274771f0f191fe867e89c590cf4ac124 Mon Sep 17 00:00:00 2001 From: Marcin Szamotulski Date: Sat, 30 Sep 2017 16:40:52 +0200 Subject: [PATCH 5/6] Added ComponentDidCatch --- bower.json | 3 ++- src/React.js | 9 +++++++-- src/React.purs | 29 ++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/bower.json b/bower.json index b1677d4..e0af836 100644 --- a/bower.json +++ b/bower.json @@ -19,7 +19,8 @@ "dependencies": { "purescript-eff": "^3.0.0", "purescript-prelude": "^3.0.0", - "purescript-unsafe-coerce": "^3.0.0" + "purescript-unsafe-coerce": "^3.0.0", + "purescript-exceptions": "^3.1.0" }, "devDependencies": { "purescript-console": "^3.0.0", diff --git a/src/React.js b/src/React.js index b4fee1e..39ffc30 100644 --- a/src/React.js +++ b/src/React.js @@ -84,7 +84,9 @@ function transformState(this_){ } exports.transformState = transformState; -function createClass(spec) { +function createClass(toNullable, spec) { + var didCatch = toNullable(spec.componentDidCatch) + var result = { displayName: spec.displayName, render: function(){ @@ -101,6 +103,9 @@ function createClass(spec) { componentDidMount: function(){ return spec.componentDidMount(this)(); }, + componentDidCatch: didCatch + ? function(error, info) {return didCatch(this)(error)(info)(); } + : undefined, componentWillReceiveProps: function(nextProps){ return spec.componentWillReceiveProps(this)(nextProps)(); }, @@ -120,7 +125,7 @@ function createClass(spec) { return createReactClass(result); } -exports.createClass = createClass; +exports["createClass'"] = createClass; function handle(f) { return function(e){ diff --git a/src/React.purs b/src/React.purs index 67a382c..2533629 100644 --- a/src/React.purs +++ b/src/React.purs @@ -25,6 +25,7 @@ module React , GetInitialState , ComponentWillMount , ComponentDidMount + , ComponentDidCatch , ComponentWillReceiveProps , ShouldComponentUpdate , ComponentWillUpdate @@ -66,7 +67,12 @@ module React ) where import Prelude + import Control.Monad.Eff (kind Effect, Eff) +import Control.Monad.Eff.Exception (Error) +import Data.Function.Uncurried (Fn2, runFn2) +import Data.Maybe (Maybe(Nothing)) +import Data.Nullable (Nullable, toNullable) import Unsafe.Coerce (unsafeCoerce) -- | Name of a tag. @@ -200,6 +206,18 @@ type ComponentDidMount props state eff = | eff ) Unit +type ComponentDidCatch props state eff = + ReactThis props state -> + Error -> + { componentStack :: String } -> + Eff + ( props :: ReactProps + , state :: ReactState ReadWrite + , refs :: ReactRefs ReadOnly + | eff + ) Unit + + -- | A component will receive props function. type ComponentWillReceiveProps props state eff = ReactThis props state -> @@ -264,6 +282,7 @@ type ReactSpec props state render eff = , getInitialState :: GetInitialState props state eff , componentWillMount :: ComponentWillMount props state eff , componentDidMount :: ComponentDidMount props state eff + , componentDidCatch :: Maybe (ComponentDidCatch props state eff) , componentWillReceiveProps :: ComponentWillReceiveProps props state eff , shouldComponentUpdate :: ShouldComponentUpdate props state eff , componentWillUpdate :: ComponentWillUpdate props state eff @@ -289,6 +308,7 @@ spec' getInitialState renderFn = , getInitialState: getInitialState , componentWillMount: \_ -> pure unit , componentDidMount: \_ -> pure unit + , componentDidCatch: Nothing , componentWillReceiveProps: \_ _ -> pure unit , shouldComponentUpdate: \_ _ _ -> pure true , componentWillUpdate: \_ _ _ -> pure unit @@ -335,8 +355,15 @@ foreign import transformState :: forall props state eff. Eff (state :: ReactState ReadWrite | eff) Unit -- | Create a React class from a specification. -foreign import createClass :: forall props state render eff. +foreign import createClass' :: forall props state render eff. + Fn2 + (forall a. Maybe a -> Nullable a) + (ReactSpec props state render eff) + (ReactClass props) + +createClass :: forall props state render eff. ReactSpec props state render eff -> ReactClass props +createClass spc = runFn2 createClass' toNullable spc -- | Create a stateless React class. createClassStateless :: forall props render. From 2c6558e1e07553e4ccbc170fd2b227cef68ac9d5 Mon Sep 17 00:00:00 2001 From: Marcin Szamotulski Date: Sun, 8 Oct 2017 22:02:10 +0200 Subject: [PATCH 6/6] displayName of stateless components --- src/React.js | 15 +++++++++++++++ src/React.purs | 12 +++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/React.js b/src/React.js index 16d0225..cfed869 100644 --- a/src/React.js +++ b/src/React.js @@ -127,6 +127,21 @@ function createClass(toNullable, spec) { } exports["createClass'"] = createClass; +function capitalize(s) { + if (!s) + return s; + return s.charAt(0).toUpperCase() + s.slice(1); +}; + +function createClassStateless(dict) { + return function (f) { + if (!f.displayName) + f.displayName = capitalize(f.name); + return f; + }; +}; +exports.createClassStateless = createClassStateless; + function forceUpdateCbImpl(this_, cb) { this_.forceUpdate(function() { return cb(); diff --git a/src/React.purs b/src/React.purs index 7429f3c..ede40a7 100644 --- a/src/React.purs +++ b/src/React.purs @@ -372,11 +372,17 @@ createClass :: forall props state render eff. ReactSpec props state render eff -> ReactClass props createClass spc = runFn2 createClass' toNullable spc --- | Create a stateless React class. -createClassStateless :: forall props render. +-- | Create a stateless React class. When using a non anonymous function the +-- | displayName will be the capitalized name of the function, e.g. +-- | ``` purescript +-- | helloWorld = createClassStatelesss hellowWorldCls +-- | where +-- | hellowWorldCls props = ... +-- | ``` +-- | Then the `displayName` will be set up to `HellowWorldCls` +foreign import createClassStateless :: forall props render. ReactRender render => (props -> render) -> ReactClass props -createClassStateless = unsafeCoerce -- | Create a stateless React class with children access. createClassStateless' :: forall props render.