Skip to content

Commit

Permalink
React 16 (#109)
Browse files Browse the repository at this point in the history
* React-16: polymorphic Render type

* stateless components

* Added ReactRender String instance.

* ReactRender: int & number

Also add `React.DOM.int` and `React.DOM.number` functions.

* Added ComponentDidCatch

* displayName of stateless components
  • Loading branch information
Marcin Szamotulski authored and ethul committed Nov 2, 2017
1 parent 356ba14 commit dbbf740
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 24 deletions.
1 change: 1 addition & 0 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"purescript-eff": "^3.0.0",
"purescript-prelude": "^3.0.0",
"purescript-unsafe-coerce": "^3.0.0",
"purescript-exceptions": "^3.1.0",
"purescript-maybe": "^3.0.0",
"purescript-nullable": "^3.0.0"
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
24 changes: 22 additions & 2 deletions src/React.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,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(){
Expand All @@ -125,6 +127,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)();
},
Expand All @@ -144,7 +149,22 @@ function createClass(spec) {

return createReactClass(result);
}
exports.createClass = createClass;
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() {
Expand Down
89 changes: 68 additions & 21 deletions src/React.purs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -25,6 +26,7 @@ module React
, GetInitialState
, ComponentWillMount
, ComponentDidMount
, ComponentDidCatch
, ComponentWillReceiveProps
, ShouldComponentUpdate
, ComponentWillUpdate
Expand Down Expand Up @@ -76,8 +78,10 @@ module React
import Prelude

import Control.Monad.Eff (kind Effect, Eff)
import Data.Maybe (Maybe)
import Data.Nullable (Nullable, toMaybe)
import Control.Monad.Eff.Exception (Error)
import Data.Function.Uncurried (Fn2, runFn2)
import Data.Maybe (Maybe(Nothing))
import Data.Nullable (Nullable, toMaybe, toNullable)
import Control.Monad.Eff.Uncurried (EffFn2, runEffFn2)
import Unsafe.Coerce (unsafeCoerce)

Expand Down Expand Up @@ -160,15 +164,27 @@ type EventHandlerContext eff props state result =
| eff
) result

class ReactRender a

instance arrayReactRender :: ReactRender (Array ReactElement)

instance reactElementReactRender :: ReactRender ReactElement

instance stringReactRender :: ReactRender String

instance intReactRender :: ReactRender Int

instance numberReactRender :: ReactRender Number

-- | 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 =
Expand Down Expand Up @@ -200,6 +216,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 ->
Expand Down Expand Up @@ -258,12 +286,13 @@ 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
, 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
Expand All @@ -272,21 +301,24 @@ 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: ""
, getInitialState: getInitialState
, componentWillMount: \_ -> pure unit
, componentDidMount: \_ -> pure unit
, componentDidCatch: Nothing
, componentWillReceiveProps: \_ _ -> pure unit
, shouldComponentUpdate: \_ _ _ -> pure true
, componentWillUpdate: \_ _ _ -> pure unit
Expand Down Expand Up @@ -357,17 +389,32 @@ 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

-- | Create a stateless React class.
createClassStateless :: forall props.
(props -> ReactElement) -> ReactClass props
createClassStateless = unsafeCoerce
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. 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

-- | 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)
Expand Down
6 changes: 6 additions & 0 deletions src/React/DOM.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down

0 comments on commit dbbf740

Please sign in to comment.