Skip to content

React 16 #109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue #118 @ethul this might be a bit surprising but it's best of what we can get.

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