@@ -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,193 @@ 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
+ ## 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