Skip to content

Commit db05c85

Browse files
authored
Additional documentation (#143)
Resolves #96 Resolves #105 Resolves #138 Resolves #139
1 parent dcfbad0 commit db05c85

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed

README.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ For a more high-level set of bindings, you might like to look at `purescript-the
1414
bower install purescript-react
1515
```
1616

17+
This library requires the `react` module. This dependency may be satisfied by installing the NPM [react package](https://www.npmjs.com/package/react).
18+
19+
```
20+
npm install react
21+
```
22+
1723
## Related Modules
1824

1925
- [React DOM](https://github.com/purescript-contrib/purescript-react-dom)
@@ -22,3 +28,189 @@ bower install purescript-react
2228
## Example
2329

2430
Please refer to [purescript-react-example](https://github.com/ethul/purescript-react-example)
31+
32+
## Troubleshooting
33+
34+
#### How to use JavaScript components?
35+
36+
To use a React component that is published as a JavaScript module, one
37+
can leverage PureScript's FFI to define a type for the component and its
38+
props. Consider the following example.
39+
40+
```purescript
41+
module Clock (clockComponent) where
42+
43+
import React (ReactClass, SyntheticEventHandler, Children)
44+
import React.SyntheticEvent (SyntheticEvent)
45+
46+
foreign import clockComponent
47+
:: ReactClass
48+
{ children :: Children
49+
, format :: String
50+
, className :: String
51+
, onTick :: SyntheticEventHandler SyntheticEvent
52+
}
53+
```
54+
55+
Rendering the `clockComponent` can be done as follows.
56+
57+
```purescript
58+
module Component where
59+
60+
import Prelude
61+
62+
import React as React
63+
64+
import Clock as Clock
65+
66+
clock :: React.ReactElement
67+
clock =
68+
React.createElement Clock.clockComponent
69+
{ format: "HH:mm:ss"
70+
, className: "test-class-name"
71+
, onTick: React.handle $ \event -> do
72+
React.preventDefault event
73+
-- etc.
74+
pure unit
75+
} []
76+
```
77+
78+
A consideration when defining a type for an external component is that
79+
some components pass their props through to a DOM element. In a case
80+
such as this, it can be helpful to leverage the props defined in the
81+
`React.DOM.Props` module. One way to accomplish this is to define the
82+
external component as follows.
83+
84+
```purescript
85+
module Clock
86+
( clockComponent
87+
, format
88+
, onTick
89+
) where
90+
91+
import Prelude
92+
93+
import React (ReactClass, ReactElement, SyntheticEventHandlerContext, Children, createElement, handle)
94+
import React.SyntheticEvent (SyntheticEvent)
95+
import React.DOM.Props (Props, unsafeFromPropsArray, unsafeMkProps)
96+
97+
clockComponent :: Array Props -> Array ReactElement -> ReactElement
98+
clockComponent props children = createElement clockComponent_ (unsafeFromPropsArray props :: {}) children
99+
100+
format :: String -> Props
101+
format = unsafeMkProps "format"
102+
103+
onTick :: forall eff props state. (SyntheticEvent -> SyntheticEventHandlerContext eff props state Unit) -> Props
104+
onTick k = unsafeMkProps "onTick" (handle k)
105+
106+
foreign import clockComponent_
107+
:: ReactClass
108+
{ children :: Children
109+
}
110+
```
111+
112+
Rendering the `clockComponent` can be done as follows.
113+
114+
```purescript
115+
module Component where
116+
117+
import Prelude
118+
119+
import React as React
120+
import React.DOM.Props as Props
121+
122+
import Clock as Clock
123+
124+
clock :: React.ReactElement
125+
clock =
126+
Clock.clockComponent
127+
[ Clock.format "HH:mm:ss"
128+
, Clock.onTick $ \event -> do
129+
React.preventDefault event
130+
-- etc.
131+
pure unit
132+
, Props.className "test-class-name"
133+
, Props.style
134+
{ fontWeight: "bold"
135+
, color: "blue"
136+
}
137+
-- additional Props.*
138+
]
139+
[ ]
140+
```
141+
142+
#### Components with type class constraints re-mount on every render?
143+
144+
Consider the following example where an ordered list component is
145+
defined for any item of type `a`, where `a` is constrained to have an
146+
`Ord` type class instance.
147+
148+
```purescript
149+
module OrderedList where
150+
151+
import Prelude
152+
153+
import Data.Array (sort)
154+
155+
import React as React
156+
import React.DOM as DOM
157+
import Debug.Trace as Trace
158+
159+
type OrderedListProps a
160+
= { items :: Array a
161+
, renderItem :: a -> React.ReactElement
162+
}
163+
164+
orderedList :: forall a. Ord a => React.ReactClass (OrderedListProps a)
165+
orderedList = React.component "OrderedList" component
166+
where
167+
component this =
168+
pure { state: {}
169+
, componentDidMount: do
170+
_ <- pure $ Trace.spy "OrderedList.componentDidMount"
171+
pure unit
172+
, render: render <$> React.getProps this
173+
}
174+
where
175+
render
176+
{ items
177+
, renderItem
178+
} =
179+
DOM.ol [ ] $
180+
renderItem' <$> sort items
181+
where
182+
renderItem' a =
183+
DOM.li
184+
[ ]
185+
[ renderItem a ]
186+
187+
-- This would be defined where the type parameter `a` is known.
188+
189+
orderedListInt :: React.ReactClass (OrderedListProps Int)
190+
orderedListInt = orderedList
191+
```
192+
193+
If the component `orderedList` above were to be rendered, the debugging
194+
statement `OrderedList.componentDidMount` is printed to the console each
195+
time the parent component is rendered. The reason for this is due to how
196+
the `orderedList` component is compiled to JavaScript.
197+
198+
```javascript
199+
var orderedList = function (dictOrd) {
200+
var component = function ($$this) {
201+
// ...
202+
};
203+
return React.component()("OrderedList")(component);
204+
};
205+
```
206+
207+
Above, the component creation is wrapped by the function with the
208+
`dictOrd` parameter. This means that a new component is being created on
209+
each render of the component using `orderedList`. This may not be ideal
210+
in all cases; e.g., if `orderedList` had needed to store state.
211+
212+
To avoid `orderedList` from being recreated each time, a function can be
213+
defined that specifies the type parameter with the type class contraint.
214+
If the component using the ordered list knows that the items are of type
215+
`Int`, the component can define `orderedListInt` as shown above, and use
216+
it to render the ordered list instead of `orderedList`.

0 commit comments

Comments
 (0)