diff --git a/README.md b/README.md
index 2d0d398..565a6ac 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
This package implements an opinionated set of bindings to the React library, optimizing for the most basic use cases.
-## Features
+## Features
- All React DOM elements and attributes are supported.
- An intuitive API for specifying props - no arrays of key value pairs, just records.
@@ -41,7 +41,8 @@ type ExampleState =
-- state update callback, and produces a document.
example :: R.ReactComponent ExampleProps
example = R.react
- { initialState: \_ -> { counter: 0 }
+ { initialState: { counter: 0 }
+ , receiveProps: \_ _ _ -> pure unit
, render: \{ label } { counter } setState ->
R.button { onClick: mkEffFn1 \_ -> do
setState { counter: counter + 1 }
diff --git a/examples/component/.gitignore b/examples/component/.gitignore
new file mode 100644
index 0000000..645684d
--- /dev/null
+++ b/examples/component/.gitignore
@@ -0,0 +1,4 @@
+output
+html/index.js
+package-lock.json
+node_modules
diff --git a/examples/component/Makefile b/examples/component/Makefile
new file mode 100644
index 0000000..a7679c9
--- /dev/null
+++ b/examples/component/Makefile
@@ -0,0 +1,5 @@
+all:
+ purs compile src/*.purs '../../src/**/*.purs' '../../bower_components/purescript-*/src/**/*.purs'
+ purs bundle --module Container output/*/*.js > output/bundle.js
+ echo 'module.exports = PS.Container;' >> output/bundle.js
+ node_modules/browserify/bin/cmd.js output/bundle.js index.js -o html/index.js
diff --git a/examples/component/README.md b/examples/component/README.md
new file mode 100644
index 0000000..cc747f4
--- /dev/null
+++ b/examples/component/README.md
@@ -0,0 +1,12 @@
+# Component Example
+
+## Building
+
+```
+npm install
+make all
+```
+
+This will compile the PureScript source files, bundle them, and use Browserify to combine PureScript and NPM sources into a single bundle.
+
+Then open `html/index.html` in your browser.
diff --git a/examples/component/html/index.html b/examples/component/html/index.html
new file mode 100644
index 0000000..6b93b7c
--- /dev/null
+++ b/examples/component/html/index.html
@@ -0,0 +1,10 @@
+
+
+
+ react-basic example
+
+
+
+
+
+
diff --git a/examples/component/index.js b/examples/component/index.js
new file mode 100644
index 0000000..1a54c75
--- /dev/null
+++ b/examples/component/index.js
@@ -0,0 +1,10 @@
+"use strict";
+
+var React = require("react");
+var ReactDOM = require("react-dom");
+var Container = require("./output/bundle.js");
+
+ReactDOM.render(
+ React.createElement(Container.component, { label: 'Increment' }),
+ document.getElementById("container")
+);
diff --git a/examples/component/package.json b/examples/component/package.json
new file mode 100644
index 0000000..8648b2b
--- /dev/null
+++ b/examples/component/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "component",
+ "version": "1.0.0",
+ "description": "",
+ "keywords": [],
+ "author": "",
+ "dependencies": {
+ "create-react-class": "^15.6.2",
+ "react": "^15.6.2",
+ "react-dom": "^15.6.2"
+ },
+ "devDependencies": {
+ "browserify": "^16.1.0"
+ }
+}
diff --git a/examples/component/src/Container.purs b/examples/component/src/Container.purs
new file mode 100644
index 0000000..8ce3150
--- /dev/null
+++ b/examples/component/src/Container.purs
@@ -0,0 +1,16 @@
+module Container where
+
+import Prelude
+
+import React.Basic as R
+import ToggleButton as ToggleButton
+
+component :: R.ReactComponent Unit
+component = R.react
+ { initialState: unit
+ , receiveProps: \_ _ _ -> pure unit
+ , render: \_ _ setState ->
+ R.div { } [ R.component ToggleButton.component { on: true }
+ , R.component ToggleButton.component { on: false }
+ ]
+ }
diff --git a/examples/component/src/ToggleButton.purs b/examples/component/src/ToggleButton.purs
new file mode 100644
index 0000000..fd11c6e
--- /dev/null
+++ b/examples/component/src/ToggleButton.purs
@@ -0,0 +1,24 @@
+module ToggleButton where
+
+import Prelude
+
+import Control.Monad.Eff.Uncurried (mkEffFn1)
+import React.Basic as R
+
+type ExampleProps =
+ { on :: Boolean
+ }
+
+type ExampleState =
+ { on :: Boolean
+ }
+
+component :: R.ReactComponent ExampleProps
+component = R.react
+ { initialState: { on: false }
+ , receiveProps: \{ on } _ setState -> setState { on }
+ , render: \_ { on } setState ->
+ R.button { onClick: mkEffFn1 \_ -> setState { on: not on }
+ }
+ [ R.text if on then "On" else "Off" ]
+ }
diff --git a/examples/counter/.gitignore b/examples/counter/.gitignore
new file mode 100644
index 0000000..645684d
--- /dev/null
+++ b/examples/counter/.gitignore
@@ -0,0 +1,4 @@
+output
+html/index.js
+package-lock.json
+node_modules
diff --git a/examples/counter/Makefile b/examples/counter/Makefile
new file mode 100644
index 0000000..a31aa7b
--- /dev/null
+++ b/examples/counter/Makefile
@@ -0,0 +1,5 @@
+all:
+ purs compile src/*.purs '../../src/**/*.purs' '../../bower_components/purescript-*/src/**/*.purs'
+ purs bundle --module Counter output/*/*.js > output/bundle.js
+ echo 'module.exports = PS.Counter;' >> output/bundle.js
+ node_modules/browserify/bin/cmd.js output/bundle.js index.js -o html/index.js
diff --git a/examples/counter/README.md b/examples/counter/README.md
new file mode 100644
index 0000000..f2418c0
--- /dev/null
+++ b/examples/counter/README.md
@@ -0,0 +1,12 @@
+# Counter Example
+
+## Building
+
+```
+npm install
+make all
+```
+
+This will compile the PureScript source files, bundle them, and use Browserify to combine PureScript and NPM sources into a single bundle.
+
+Then open `html/index.html` in your browser.
diff --git a/examples/counter/html/index.html b/examples/counter/html/index.html
new file mode 100644
index 0000000..6b93b7c
--- /dev/null
+++ b/examples/counter/html/index.html
@@ -0,0 +1,10 @@
+
+
+
+ react-basic example
+
+
+
+
+
+
diff --git a/examples/counter/index.js b/examples/counter/index.js
new file mode 100644
index 0000000..4fbcf78
--- /dev/null
+++ b/examples/counter/index.js
@@ -0,0 +1,10 @@
+"use strict";
+
+var React = require("react");
+var ReactDOM = require("react-dom");
+var Counter = require("./output/bundle.js");
+
+ReactDOM.render(
+ React.createElement(Counter.component, { label: 'Increment' }),
+ document.getElementById("container")
+);
diff --git a/examples/counter/package.json b/examples/counter/package.json
new file mode 100644
index 0000000..b1ac6d8
--- /dev/null
+++ b/examples/counter/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "counter",
+ "version": "1.0.0",
+ "description": "",
+ "keywords": [],
+ "author": "",
+ "dependencies": {
+ "create-react-class": "^15.6.2",
+ "react": "^15.6.2",
+ "react-dom": "^15.6.2"
+ },
+ "devDependencies": {
+ "browserify": "^16.1.0"
+ }
+}
diff --git a/examples/counter/src/Counter.purs b/examples/counter/src/Counter.purs
new file mode 100644
index 0000000..724d4d6
--- /dev/null
+++ b/examples/counter/src/Counter.purs
@@ -0,0 +1,30 @@
+module Counter where
+
+import Prelude
+
+import Control.Monad.Eff.Uncurried (mkEffFn1)
+import React.Basic as R
+
+-- The props for the component
+type ExampleProps =
+ { label :: String
+ }
+
+-- The internal state of the component
+type ExampleState =
+ { counter :: Int
+ }
+
+-- Create a component by passing a record to the `react` function.
+-- The `render` function takes the props and current state, as well as a
+-- state update callback, and produces a document.
+component :: R.ReactComponent ExampleProps
+component = R.react
+ { initialState: { counter: 0 }
+ , receiveProps: \_ _ _ -> pure unit
+ , render: \{ label } { counter } setState ->
+ R.button { onClick: mkEffFn1 \_ -> do
+ setState { counter: counter + 1 }
+ }
+ [ R.text (label <> ": " <> show counter) ]
+ }
diff --git a/generated-docs/React/Basic.md b/generated-docs/React/Basic.md
index 7b1ec97..bebdf3e 100644
--- a/generated-docs/React/Basic.md
+++ b/generated-docs/React/Basic.md
@@ -3,19 +3,27 @@
#### `react`
``` purescript
-react :: forall props state. { initialState :: state, render :: props -> state -> (state -> Eff (react :: ReactFX) Unit) -> JSX } -> ReactComponent props
+react :: forall props state. { initialState :: state, receiveProps :: props -> state -> (state -> Eff (react :: ReactFX) Unit) -> Eff (react :: ReactFX) Unit, render :: props -> state -> (state -> Eff (react :: ReactFX) Unit) -> JSX } -> ReactComponent props
```
Create a React component from a _specification_ of that component.
A _specification_ consists of a state type, an initial value for that state,
-and a rendering function which takes a value of that state type, additional
-_props_ (which will be passed in by the user) and a state update function.
+a function to apply incoming props to the internal state, and a rendering
+function which takes props, state and a state update function.
The rendering function should return a value of type `JSX`, which can be
constructed using the helper functions provided by the `React.Basic.DOM`
module (and re-exported here).
+#### `component`
+
+``` purescript
+component :: forall props. ReactComponent props -> props -> JSX
+```
+
+Create a `JSX` node from another React component, by providing the props.
+
### Re-exported from React.Basic.DOM:
diff --git a/src/React/Basic.js b/src/React/Basic.js
index 02727ca..d5b68bc 100644
--- a/src/React/Basic.js
+++ b/src/React/Basic.js
@@ -5,7 +5,23 @@ var React = require('react');
exports.react_ = function(spec) {
return React.createClass({
getInitialState: function() {
- return spec.initialState(this.props);
+ return spec.initialState;
+ },
+ componentDidMount: function() {
+ var this_ = this;
+ spec.receiveProps(this.props, this.state, function(newState) {
+ return function() {
+ this_.setState(newState);
+ };
+ });
+ },
+ componentWillReceiveProps: function(newProps) {
+ var this_ = this;
+ spec.receiveProps(newProps, this.state, function(newState) {
+ return function() {
+ this_.setState(newState);
+ };
+ });
},
render: function() {
var this_ = this;
@@ -17,3 +33,7 @@ exports.react_ = function(spec) {
}
});
};
+
+exports.component_ = function(component, props) {
+ return React.createElement(component, props);
+}
diff --git a/src/React/Basic.purs b/src/React/Basic.purs
index e12e2d0..0ee2cdd 100644
--- a/src/React/Basic.purs
+++ b/src/React/Basic.purs
@@ -1,5 +1,6 @@
module React.Basic
( react
+ , component
, module React.Basic.DOM
, module React.Basic.Types
) where
@@ -7,14 +8,16 @@ module React.Basic
import Prelude
import Control.Monad.Eff (Eff, kind Effect)
-import Data.Function.Uncurried (Fn3, mkFn3)
+import Control.Monad.Eff.Uncurried (EffFn3, mkEffFn3)
+import Data.Function.Uncurried (Fn2, runFn2, Fn3, mkFn3)
import React.Basic.DOM as React.Basic.DOM
import React.Basic.Types (CSS, EventHandler, JSX, ReactComponent, ReactFX)
import React.Basic.Types as React.Basic.Types
foreign import react_
:: forall props state
- . { initialState :: props -> state
+ . { initialState :: state
+ , receiveProps :: EffFn3 (react :: ReactFX) props state (state -> Eff (react :: ReactFX) Unit) Unit
, render :: Fn3 props state (state -> Eff (react :: ReactFX) Unit) JSX
}
-> ReactComponent props
@@ -22,16 +25,32 @@ foreign import react_
-- | Create a React component from a _specification_ of that component.
-- |
-- | A _specification_ consists of a state type, an initial value for that state,
--- | and a rendering function which takes a value of that state type, additional
--- | _props_ (which will be passed in by the user) and a state update function.
+-- | a function to apply incoming props to the internal state, and a rendering
+-- | function which takes props, state and a state update function.
-- |
-- | The rendering function should return a value of type `JSX`, which can be
-- | constructed using the helper functions provided by the `React.Basic.DOM`
-- | module (and re-exported here).
react
:: forall props state
- . { initialState :: props -> state
+ . { initialState :: state
+ , receiveProps :: props -> state -> (state -> Eff (react :: ReactFX) Unit) -> Eff (react :: ReactFX) Unit
, render :: props -> state -> (state -> Eff (react :: ReactFX) Unit) -> JSX
}
-> ReactComponent props
-react { initialState, render } = react_ { initialState, render: mkFn3 render }
+react { initialState, receiveProps, render } =
+ react_
+ { initialState
+ , receiveProps: mkEffFn3 receiveProps
+ , render: mkFn3 render
+ }
+
+foreign import component_ :: forall props. Fn2 (ReactComponent props) props JSX
+
+-- | Create a `JSX` node from another React component, by providing the props.
+component
+ :: forall props
+ . ReactComponent props
+ -> props
+ -> JSX
+component = runFn2 component_