From e6b4697311b931352fdada5a797749ecd05b1cf1 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 14 Apr 2018 12:51:29 -0400 Subject: [PATCH 1/6] Additional documentation Resolves #96 Resolves #105 Resolves #138 Resolves #139 --- README.md | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/README.md b/README.md index 0231486..dc07c01 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,12 @@ For a more high-level set of bindings, you might like to look at `purescript-the bower install purescript-react ``` +This library requires the `react` module. This dependency may be satisfied by installing the NPM [react package](https://www.npmjs.com/package/react). + +``` +npm install react +``` + ## Related Modules - [React DOM](https://github.com/purescript-contrib/purescript-react-dom) @@ -22,3 +28,193 @@ bower install purescript-react ## Example Please refer to [purescript-react-example](https://github.com/ethul/purescript-react-example) + +## External Components + +To use a React component that is not published as a PureScript module, +one can leverage PureScript's FFI to define a type for component and its +props. Consider the following example. + +```purescript +module Clock (clockComponent) where + +import React (ReactClass, SyntheticEventHandler, Children) +import React.SyntheticEvent (SyntheticEvent) + +foreign import clockComponent + :: ReactClass + { children :: Children + , format :: String + , className :: String + , onTick :: SyntheticEventHandler SyntheticEvent + } +``` + +Rendering the `clockComponent` can be done as follows. + +```purescript +module Component where + +import Prelude + +import React as React + +import Clock as Clock + +clock :: React.ReactElement +clock = + React.createElement Clock.clockComponent + { format: "HH:mm:ss" + , className: "test-class-name" + , onTick: React.handle $ \event -> do + React.preventDefault event + -- etc. + pure unit + } [] +``` + +A consideration when defining a type for an external component is that +some components pass their props through to a DOM element. In a case +such as this, it can be helpful to leverage the props defined in the +`React.DOM.Props` module. + +One way to accomplish this is to define the external component as +follows. + +```purescript +module Clock + ( clockComponent + , format + , onTick + ) where + +import Prelude + +import React (ReactClass, ReactElement, SyntheticEventHandlerContext, Children, createElement, handle) +import React.SyntheticEvent (SyntheticEvent) +import React.DOM.Props (Props, unsafeFromPropsArray, unsafeMkProps) + +clockComponent :: Array Props -> Array ReactElement -> ReactElement +clockComponent props children = createElement clockComponent_ (unsafeFromPropsArray props :: {}) children + +format :: String -> Props +format = unsafeMkProps "format" + +onTick :: forall eff props state. (SyntheticEvent -> SyntheticEventHandlerContext eff props state Unit) -> Props +onTick k = unsafeMkProps "onTick" (handle k) + +foreign import clockComponent_ + :: ReactClass + { children :: Children + } +``` + +Rendering the `clockComponent` can be done as follows. + +```purescript +module Component where + +import Prelude + +import React as React +import React.DOM.Props as Props + +import Clock as Clock + +clock :: React.ReactElement +clock = + Clock.clockComponent + [ Clock.format "HH:mm:ss" + , Clock.onTick $ \event -> do + React.preventDefault event + -- etc. + pure unit + , Props.className "test-class-name" + , Props.style + { fontWeight: "bold" + , color: "blue" + } + -- additional Props.* + ] + [ ] +``` + +## Troubleshooting + +#### Component with type class constraints re-mounting on every render? + +Consider the following example where an ordered list component is +defined for any item of type `a`, where `a` is constrained to have a +type class instance `Ord`. + +```purescript +module OrderedList where + +import Prelude + +import Data.Array (sort) + +import React as React +import React.DOM as DOM +import Debug.Trace as Trace + +type OrderedListProps a + = { items :: Array a + , renderItem :: a -> React.ReactElement + } + +orderedList :: forall a. Ord a => React.ReactClass (OrderedListProps a) +orderedList = React.component "OrderedList" component + where + component this = + pure { state: {} + , componentDidMount: do + _ <- pure $ Trace.spy "OrderedList.componentDidMount" + pure unit + , render: render <$> React.getProps this + } + where + render + { items + , renderItem + } = + DOM.ol [ ] $ + renderItem' <$> sort items + where + renderItem' a = + DOM.li + [ ] + [ renderItem a ] + +-- This may be defined elsewhere where the type parameter `a` is known. +-- In this case it is defined to be `Int`. + +orderedListInt :: React.ReactClass (OrderedListProps Int) +orderedListInt = orderedList +``` + +If the component `orderedList` above where to be rendered, the debugging +statement `OrderedList.componentDidMount` is printed to the console each +time the parent component is rendered. The reason for this is due to how +the `orderedList` component is compiled to JavaScript. + +```javascript +var orderedList = function (dictOrd) { + var component = function ($$this) { + // ... + }; + return React.component()("OrderedList")(component); +}; +``` + +Above, the component is re-created each time due to the function with +the `dictOrd` parameter wrapping the component. This means that a new +component is being recreated on each render of the component using +`orderedList`. This may not be ideal in all cases; e.g., if +`orderedList` needed to store state. + +To avoid `orderedList` from being re-created each time, a function can +be defined that specifies the type parameter. If the component using the +ordered list knows that the elements are of type `Int`, the component +can define `orderedListInt` as above and use that to render the ordered +list instead of `orderedList`. From 64c9416ad2a18b3f69810e8dbc3e698a284c539e Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 14 Apr 2018 12:55:44 -0400 Subject: [PATCH 2/6] Update docs --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index dc07c01..d9daaa8 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,12 @@ npm install react Please refer to [purescript-react-example](https://github.com/ethul/purescript-react-example) -## External Components +## Troubleshooting + +#### How to use JavaScript components? -To use a React component that is not published as a PureScript module, -one can leverage PureScript's FFI to define a type for component and its +To use a React component that is published as a JavaScript module one +can leverage PureScript's FFI to define a type for component and its props. Consider the following example. ```purescript @@ -139,9 +141,7 @@ clock = [ ] ``` -## Troubleshooting - -#### Component with type class constraints re-mounting on every render? +#### Components with type class constraints re-mount on every render? Consider the following example where an ordered list component is defined for any item of type `a`, where `a` is constrained to have a From c62d2761abc79319d6277ee240bc072918801292 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 14 Apr 2018 13:03:52 -0400 Subject: [PATCH 3/6] Fix typos --- README.md | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index d9daaa8..295d9c6 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ Please refer to [purescript-react-example](https://github.com/ethul/purescript-r #### How to use JavaScript components? -To use a React component that is published as a JavaScript module one -can leverage PureScript's FFI to define a type for component and its +To use a React component that is published as a JavaScript module, one +can leverage PureScript's FFI to define a type for the component and its props. Consider the following example. ```purescript @@ -78,10 +78,8 @@ clock = A consideration when defining a type for an external component is that some components pass their props through to a DOM element. In a case such as this, it can be helpful to leverage the props defined in the -`React.DOM.Props` module. - -One way to accomplish this is to define the external component as -follows. +`React.DOM.Props` module. One way to accomplish this is to define the +external component as follows. ```purescript module Clock @@ -193,7 +191,7 @@ orderedListInt :: React.ReactClass (OrderedListProps Int) orderedListInt = orderedList ``` -If the component `orderedList` above where to be rendered, the debugging +If the component `orderedList` above were to be rendered, the debugging statement `OrderedList.componentDidMount` is printed to the console each time the parent component is rendered. The reason for this is due to how the `orderedList` component is compiled to JavaScript. @@ -207,14 +205,13 @@ var orderedList = function (dictOrd) { }; ``` -Above, the component is re-created each time due to the function with -the `dictOrd` parameter wrapping the component. This means that a new -component is being recreated on each render of the component using -`orderedList`. This may not be ideal in all cases; e.g., if -`orderedList` needed to store state. - -To avoid `orderedList` from being re-created each time, a function can -be defined that specifies the type parameter. If the component using the -ordered list knows that the elements are of type `Int`, the component -can define `orderedListInt` as above and use that to render the ordered -list instead of `orderedList`. +Above, the component creation is wrapped by the function with the +`dictOrd` parameter. This means that a new component is being recreated +on each render of the component using `orderedList`. This may not be +ideal in all cases; e.g., if `orderedList` had needed to store state. + +To avoid `orderedList` from being recreated each time, a function can be +defined that specifies the type parameter with the type class contraint. +If the component using the ordered list knows that the elements are of +type `Int`, the component can define `orderedListInt` as shown above, +and use it to render the ordered list instead of `orderedList`. From 5e7d14c6ab17642e5722ca52a7666ff519a06555 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 14 Apr 2018 13:06:05 -0400 Subject: [PATCH 4/6] Fix typos --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 295d9c6..73f6bf8 100644 --- a/README.md +++ b/README.md @@ -142,8 +142,8 @@ clock = #### Components with type class constraints re-mount on every render? Consider the following example where an ordered list component is -defined for any item of type `a`, where `a` is constrained to have a -type class instance `Ord`. +defined for any item of type `a`, where `a` is constrained to have an +`Ord` type class instance. ```purescript module OrderedList where @@ -184,8 +184,7 @@ orderedList = React.component "OrderedList" component [ ] [ renderItem a ] --- This may be defined elsewhere where the type parameter `a` is known. --- In this case it is defined to be `Int`. +-- This would be defined where the type parameter `a` is known. orderedListInt :: React.ReactClass (OrderedListProps Int) orderedListInt = orderedList From d8aa6ae1830a724ad003ff5d7e19530463970362 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 14 Apr 2018 13:07:19 -0400 Subject: [PATCH 5/6] Fix typos --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 73f6bf8..034f2ac 100644 --- a/README.md +++ b/README.md @@ -205,9 +205,9 @@ var orderedList = function (dictOrd) { ``` Above, the component creation is wrapped by the function with the -`dictOrd` parameter. This means that a new component is being recreated -on each render of the component using `orderedList`. This may not be -ideal in all cases; e.g., if `orderedList` had needed to store state. +`dictOrd` parameter. This means that a new component is being created on +each render of the component using `orderedList`. This may not be ideal +in all cases; e.g., if `orderedList` had needed to store state. To avoid `orderedList` from being recreated each time, a function can be defined that specifies the type parameter with the type class contraint. From 5ec4bcc40a4432e73aac0e8fc776bda203a024b0 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 14 Apr 2018 13:08:47 -0400 Subject: [PATCH 6/6] Fix typos --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 034f2ac..61e34ee 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,6 @@ in all cases; e.g., if `orderedList` had needed to store state. To avoid `orderedList` from being recreated each time, a function can be defined that specifies the type parameter with the type class contraint. -If the component using the ordered list knows that the elements are of -type `Int`, the component can define `orderedListInt` as shown above, -and use it to render the ordered list instead of `orderedList`. +If the component using the ordered list knows that the items are of type +`Int`, the component can define `orderedListInt` as shown above, and use +it to render the ordered list instead of `orderedList`.