@@ -14,6 +14,12 @@ For a more high-level set of bindings, you might like to look at `purescript-the
14
14
bower install purescript-react
15
15
```
16
16
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
+
17
23
## Related Modules
18
24
19
25
- [ React DOM] ( https://github.com/purescript-contrib/purescript-react-dom )
@@ -22,3 +28,189 @@ bower install purescript-react
22
28
## Example
23
29
24
30
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