Skip to content

Commit e6b4697

Browse files
committed
Additional documentation
Resolves #96 Resolves #105 Resolves #138 Resolves #139
1 parent dcfbad0 commit e6b4697

File tree

1 file changed

+196
-0
lines changed

1 file changed

+196
-0
lines changed

README.md

+196
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,193 @@ bower install purescript-react
2228
## Example
2329

2430
Please refer to [purescript-react-example](https://github.com/ethul/purescript-react-example)
31+
32+
## External Components
33+
34+
To use a React component that is not published as a PureScript module,
35+
one can leverage PureScript's FFI to define a type for component and its
36+
props. Consider the following example.
37+
38+
```purescript
39+
module Clock (clockComponent) where
40+
41+
import React (ReactClass, SyntheticEventHandler, Children)
42+
import React.SyntheticEvent (SyntheticEvent)
43+
44+
foreign import clockComponent
45+
:: ReactClass
46+
{ children :: Children
47+
, format :: String
48+
, className :: String
49+
, onTick :: SyntheticEventHandler SyntheticEvent
50+
}
51+
```
52+
53+
Rendering the `clockComponent` can be done as follows.
54+
55+
```purescript
56+
module Component where
57+
58+
import Prelude
59+
60+
import React as React
61+
62+
import Clock as Clock
63+
64+
clock :: React.ReactElement
65+
clock =
66+
React.createElement Clock.clockComponent
67+
{ format: "HH:mm:ss"
68+
, className: "test-class-name"
69+
, onTick: React.handle $ \event -> do
70+
React.preventDefault event
71+
-- etc.
72+
pure unit
73+
} []
74+
```
75+
76+
A consideration when defining a type for an external component is that
77+
some components pass their props through to a DOM element. In a case
78+
such as this, it can be helpful to leverage the props defined in the
79+
`React.DOM.Props` module.
80+
81+
One way to accomplish this is to define the external component as
82+
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+
## Troubleshooting
143+
144+
#### Component with type class constraints re-mounting on every render?
145+
146+
Consider the following example where an ordered list component is
147+
defined for any item of type `a`, where `a` is constrained to have a
148+
type class instance `Ord`.
149+
150+
```purescript
151+
module OrderedList where
152+
153+
import Prelude
154+
155+
import Data.Array (sort)
156+
157+
import React as React
158+
import React.DOM as DOM
159+
import Debug.Trace as Trace
160+
161+
type OrderedListProps a
162+
= { items :: Array a
163+
, renderItem :: a -> React.ReactElement
164+
}
165+
166+
orderedList :: forall a. Ord a => React.ReactClass (OrderedListProps a)
167+
orderedList = React.component "OrderedList" component
168+
where
169+
component this =
170+
pure { state: {}
171+
, componentDidMount: do
172+
_ <- pure $ Trace.spy "OrderedList.componentDidMount"
173+
pure unit
174+
, render: render <$> React.getProps this
175+
}
176+
where
177+
render
178+
{ items
179+
, renderItem
180+
} =
181+
DOM.ol [ ] $
182+
renderItem' <$> sort items
183+
where
184+
renderItem' a =
185+
DOM.li
186+
[ ]
187+
[ renderItem a ]
188+
189+
-- This may be defined elsewhere where the type parameter `a` is known.
190+
-- In this case it is defined to be `Int`.
191+
192+
orderedListInt :: React.ReactClass (OrderedListProps Int)
193+
orderedListInt = orderedList
194+
```
195+
196+
If the component `orderedList` above where to be rendered, the debugging
197+
statement `OrderedList.componentDidMount` is printed to the console each
198+
time the parent component is rendered. The reason for this is due to how
199+
the `orderedList` component is compiled to JavaScript.
200+
201+
```javascript
202+
var orderedList = function (dictOrd) {
203+
var component = function ($$this) {
204+
// ...
205+
};
206+
return React.component()("OrderedList")(component);
207+
};
208+
```
209+
210+
Above, the component is re-created each time due to the function with
211+
the `dictOrd` parameter wrapping the component. This means that a new
212+
component is being recreated on each render of the component using
213+
`orderedList`. This may not be ideal in all cases; e.g., if
214+
`orderedList` needed to store state.
215+
216+
To avoid `orderedList` from being re-created each time, a function can
217+
be defined that specifies the type parameter. If the component using the
218+
ordered list knows that the elements are of type `Int`, the component
219+
can define `orderedListInt` as above and use that to render the ordered
220+
list instead of `orderedList`.

0 commit comments

Comments
 (0)