-
Notifications
You must be signed in to change notification settings - Fork 65
/
Copy pathReact.purs
481 lines (407 loc) · 15.1 KB
/
React.purs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
-- | This module defines foreign types and functions which wrap React's functionality.
module React
( TagName
, ReactComponent
, ReactThis
, ReactUnusedSnapshot
, SyntheticEventHandler
, Render
, ComponentWillMount
, ComponentDidMount
, ComponentDidCatch
, ComponentWillReceiveProps
, ShouldComponentUpdate
, ComponentWillUpdate
, ComponentDidUpdate
, GetSnapshotBeforeUpdate
, ComponentWillUnmount
, ReactSpecRequired
, ReactSpecUnsafe
, ReactSpecOptional
, ReactSpecShouldComponentUpdate
, ReactSpecAll
, ReactSpecPure
, ReactClassConstructor
, class ReactComponentSpec
, class ReactPureComponentSpec
, component
, componentWithDerivedState
, pureComponent
, pureComponentWithDerivedState
, statelessComponent
, getProps
, getState
, setState
, setStateWithCallback
, writeState
, writeStateWithCallback
, modifyState
, modifyStateWithCallback
, forceUpdate
, forceUpdateWithCallback
, class ReactPropFields
, ReservedReactPropFields
, createElement
, unsafeCreateElement
, createElementDynamic
, unsafeCreateElementDynamic
, createLeafElement
, unsafeCreateLeafElement
, createElementTagName
, createElementTagNameDynamic
, fragmentWithKey
, createHookElement
, unsafeCreateHookElement
, createHookElementDynamic
, unsafeCreateHookElementDynamic
, createHookLeafElement
, unsafeCreateHookLeafElement
, createRenderPropsElement
, module Exports
) where
import Prelude
import Effect (Effect)
import Effect.Exception (Error)
import Effect.Uncurried (EffectFn1)
import Prim.Row as Row
import Type.Row (type (+))
import Unsafe.Coerce (unsafeCoerce)
import React.Hook (Hook)
import React.Ref (Ref, DOMRef)
import React.Types (ReactElement, ReactClass, Children)
import React.Types
( ReactClass
, ReactElement
, class IsReactElement
, toElement
, Children
, childrenToArray
, childrenCount
) as Exports
-- | Name of a tag.
type TagName = String
-- | A mounted react component
foreign import data ReactComponent :: Type
-- | A reference to a component, essentially React's `this`.
foreign import data ReactThis :: Type -> Type -> Type
foreign import data ReactUnusedSnapshot :: Type
type SyntheticEventHandler event = EffectFn1 event Unit
-- | A render effect.
type Render = Effect ReactElement
-- | A component will mount effect.
type ComponentWillMount = Effect Unit
-- | A component did mount effect.
type ComponentDidMount = Effect Unit
-- | A component did catch effect.
type ComponentDidCatch = Error -> { componentStack :: String } -> Effect Unit
-- | A component will receive props function.
type ComponentWillReceiveProps props = props -> Effect Unit
-- | A should component update function.
type ShouldComponentUpdate props state = props -> state -> Effect Boolean
-- | A component will update function.
type ComponentWillUpdate props state = props -> state -> Effect Unit
-- | A component did update function.
type ComponentDidUpdate props state snapshot = props -> state -> snapshot -> Effect Unit
type GetSnapshotBeforeUpdate props state snapshot = props -> state -> Effect snapshot
-- | A component will unmount effect.
type ComponentWillUnmount = Effect Unit
-- | Required fields for constructing a ReactClass.
type ReactSpecRequired state r =
( state :: state
, render :: Render
| r
)
type ReactSpecUnsafe props state r =
( unsafeComponentWillMount :: ComponentWillMount
, unsafeComponentWillReceiveProps :: ComponentWillReceiveProps props
, unsafeComponentWillUpdate :: ComponentWillUpdate props state
| r
)
-- | Optional fields for constructing a ReactClass.
type ReactSpecOptional props state snapshot r =
( componentDidMount :: ComponentDidMount
, componentDidCatch :: ComponentDidCatch
, componentWillUnmount :: ComponentWillUnmount
, componentDidUpdate :: ComponentDidUpdate props state snapshot
, getSnapshotBeforeUpdate :: GetSnapshotBeforeUpdate props state snapshot
| ReactSpecUnsafe props state r
)
type ReactSpecShouldComponentUpdate props state =
( shouldComponentUpdate :: ShouldComponentUpdate props state
)
type ReactSpecAll props state snapshot
= ReactSpecRequired state
+ ReactSpecOptional props state snapshot
+ ReactSpecShouldComponentUpdate props state
type ReactSpecPure props state snapshot
= ReactSpecRequired state
+ ReactSpecOptional props state snapshot ()
-- | The signature for a ReactClass constructor. A constructor takes the
-- | `ReactThis` context and returns a record with appropriate lifecycle
-- | methods.
type ReactClassConstructor props state r =
ReactThis props state ->
Effect (Record r)
class ReactComponentSpec props state snapshot (given :: # Type) (spec :: # Type)
instance reactComponentSpec ::
( Row.Union given (ReactSpecAll props state ReactUnusedSnapshot) spec
, Row.Nub spec (ReactSpecAll props state snapshot)
) =>
ReactComponentSpec props state snapshot given spec
class ReactPureComponentSpec props state snapshot (given :: # Type) (spec :: # Type)
instance reactPureComponentSpec ::
( Row.Union given (ReactSpecPure props state ReactUnusedSnapshot) spec
, Row.Nub spec (ReactSpecPure props state snapshot)
) =>
ReactPureComponentSpec props state snapshot given spec
-- | Creates a `ReactClass`` inherited from `React.Component`.
component :: forall props state snapshot given spec.
ReactComponentSpec (Record props) (Record state) snapshot given spec =>
String ->
ReactClassConstructor (Record props) (Record state) given ->
ReactClass (Record props)
component = componentImpl
-- | Like `component`, but takes a `getDerivedStateFromProps` handler.
componentWithDerivedState :: forall props state snapshot given spec.
ReactComponentSpec (Record props) (Record state) snapshot given spec =>
String ->
(Record props -> Record state -> Record state) ->
ReactClassConstructor (Record props) (Record state) given ->
ReactClass (Record props)
componentWithDerivedState = componentWithDerivedStateImpl
-- | Creates a `ReactClass`` inherited from `React.PureComponent`.
pureComponent :: forall props state snapshot given spec.
ReactPureComponentSpec (Record props) (Record state) snapshot given spec =>
String ->
ReactClassConstructor (Record props) (Record state) given ->
ReactClass (Record props)
pureComponent = pureComponentImpl
-- | Like `pureComponent`, but takes a `getDerivedStateFromProps` handler.
pureComponentWithDerivedState :: forall props state snapshot given spec.
ReactPureComponentSpec (Record props) (Record state) snapshot given spec =>
String ->
(Record props -> Record state -> Record state) ->
ReactClassConstructor (Record props) (Record state) given ->
ReactClass (Record props)
pureComponentWithDerivedState = componentWithDerivedStateImpl
foreign import componentImpl :: forall this props r.
String ->
(this -> Effect r) ->
ReactClass props
foreign import componentWithDerivedStateImpl :: forall this props state r.
String ->
(props -> state -> state) ->
(this -> Effect r) ->
ReactClass props
foreign import pureComponentImpl :: forall this props r.
String ->
(this -> Effect r) ->
ReactClass props
foreign import pureComponentWithDerivedStateImpl :: forall this props state r.
String ->
(props -> state -> state) ->
(this -> Effect r) ->
ReactClass props
foreign import statelessComponent :: forall props.
(Record props -> ReactElement) ->
ReactClass (Record props)
foreign import fragment :: ReactClass { children :: Children }
-- | Read the component props.
foreign import getProps :: forall props state.
ReactThis props state ->
Effect props
foreign import setStateImpl :: forall props state update.
ReactThis props state ->
update ->
Effect Unit
foreign import setStateWithCallbackImpl :: forall props state update.
ReactThis props state ->
update ->
Effect Unit ->
Effect Unit
-- | Get the component state.
foreign import getState :: forall props state.
ReactThis props state ->
Effect state
-- | Update component state given some sub-set of state properties.
setState :: forall props given rest all.
Row.Union given rest all =>
ReactThis props (Record all) ->
Record given ->
Effect Unit
setState = setStateImpl
-- | Update component state given some sub-set of state properties, while
-- | also invoking a callback when applied.
setStateWithCallback :: forall props given rest all.
Row.Union given rest all =>
ReactThis props (Record all) ->
Record given ->
Effect Unit ->
Effect Unit
setStateWithCallback = setStateWithCallbackImpl
-- | Update component state.
writeState :: forall props all.
ReactThis props (Record all) ->
Record all ->
Effect Unit
writeState = setStateImpl
-- | Update component state, while also invoking a callback when applied.
writeStateWithCallback :: forall props all.
ReactThis props (Record all) ->
Record all ->
Effect Unit ->
Effect Unit
writeStateWithCallback = setStateWithCallbackImpl
-- | Update component state given a modification function.
modifyState :: forall props state.
ReactThis props state ->
(state -> state) ->
Effect Unit
modifyState = setStateImpl
-- | Update component state given a modification function, while also invoking
-- | a callback when applied.
modifyStateWithCallback :: forall props state.
ReactThis props state ->
(state -> state) ->
Effect Unit ->
Effect Unit
modifyStateWithCallback = setStateWithCallbackImpl
-- | Force render of a react component.
forceUpdate :: forall props state. ReactThis props state -> Effect Unit
forceUpdate this = forceUpdateWithCallback this (pure unit)
-- | Force render and then run an Effect.
foreign import forceUpdateWithCallback :: forall props state.
ReactThis props state ->
Effect Unit ->
Effect Unit
class ReactPropFields (required :: # Type) (given :: # Type)
type ReservedReactPropFields r =
( key :: String
, ref :: Ref DOMRef
| r
)
instance reactPropFields ::
( Row.Union given optional (ReservedReactPropFields required)
, Row.Union optional leftover (ReservedReactPropFields ())
) =>
ReactPropFields required given
-- | Create an element from a React class spreading the children array. Used when the children are known up front.
createElement :: forall required given.
ReactPropFields required given =>
ReactClass { children :: Children | required } ->
{ | given } ->
Array ReactElement ->
ReactElement
createElement = createElementImpl
-- | An unsafe version of `createElement` which does not enforce the reserved
-- | properties "key" and "ref".
unsafeCreateElement :: forall props.
ReactClass { children :: Children | props } ->
{ | props } ->
Array ReactElement ->
ReactElement
unsafeCreateElement = createElementImpl
-- | Create an element from a React class passing the children array. Used for a dynamic array of children.
createElementDynamic :: forall required given.
ReactPropFields required given =>
ReactClass { children :: Children | required } ->
{ | given } ->
Array ReactElement ->
ReactElement
createElementDynamic = createElementDynamicImpl
-- | An unsafe version of `createElementDynamic` which does not enforce the reserved
-- | properties "key" and "ref".
unsafeCreateElementDynamic :: forall props.
ReactClass { children :: Children | props } ->
{ | props } ->
Array ReactElement ->
ReactElement
unsafeCreateElementDynamic = createElementDynamicImpl
foreign import createElementImpl :: forall required given children.
ReactClass required -> given -> children -> ReactElement
foreign import createElementDynamicImpl :: forall required given children.
ReactClass required -> given -> Array children -> ReactElement
-- | Create an element from a React class that does not require children.
createLeafElement :: forall required given.
ReactPropFields required given =>
ReactClass { | required } ->
{ | given } ->
ReactElement
createLeafElement = createLeafElementImpl
-- | An unsafe version of `createLeafElement` which does not enforce the reserved
-- | properties "key" and "ref".
unsafeCreateLeafElement :: forall props.
ReactClass props ->
props ->
ReactElement
unsafeCreateLeafElement = createLeafElementImpl
foreign import createLeafElementImpl :: forall required given.
ReactClass required -> given -> ReactElement
-- | Create an element from a tag name spreading the children array. Used when the children are known up front.
foreign import createElementTagName :: forall props.
TagName -> props -> Array ReactElement -> ReactElement
-- | Create an element from a tag name passing the children array. Used for a dynamic array of children.
foreign import createElementTagNameDynamic :: forall props.
TagName -> props -> Array ReactElement -> ReactElement
-- | Creates a keyed fragment.
fragmentWithKey :: String -> Array ReactElement -> ReactElement
fragmentWithKey = createElement fragment <<< { key: _ }
-- | Create an element from a function using Hooks spreading the children array. Used when the children are known up front.
createHookElement
:: forall required given
. ReactPropFields required given
=> ({ children :: Children | required } -> Hook ReactElement)
-> { | given }
-> Array ReactElement
-> ReactElement
createHookElement k = createElementImpl (unsafeCoerce k)
-- | An unsafe version of `createHookElement` which does not enforce the reserved properties "key" and "ref".
unsafeCreateHookElement
:: forall props
. ({ children :: Children | props } -> Hook ReactElement)
-> { | props }
-> Array ReactElement
-> ReactElement
unsafeCreateHookElement k = createElementImpl (unsafeCoerce k)
-- | Create an element from a function using Hooks passing the children array. Used for a dynamic array of children.
createHookElementDynamic
:: forall required given
. ReactPropFields required given
=> ({ children :: Children | required } -> Hook ReactElement)
-> { | given }
-> Array ReactElement
-> ReactElement
createHookElementDynamic k = createElementDynamicImpl (unsafeCoerce k)
-- | An unsafe version of `createHookElementDynamic` which does not enforce the reserved properties "key" and "ref".
unsafeCreateHookElementDynamic
:: forall props
. ({ children :: Children | props } -> Hook ReactElement)
-> { | props }
-> Array ReactElement
-> ReactElement
unsafeCreateHookElementDynamic k = createElementDynamicImpl (unsafeCoerce k)
-- | Create an element from a function using Hooks that does not require children.
createHookLeafElement
:: forall required given
. ReactPropFields required given
=> ({ | required } -> Hook ReactElement)
-> { | given }
-> ReactElement
createHookLeafElement k = createLeafElementImpl (unsafeCoerce k)
-- | An unsafe version of `createHookLeafElement` which does not enforce the reserved
-- | properties "key" and "ref".
unsafeCreateHookLeafElement
:: forall props
. ({ | props } -> Hook ReactElement)
-> { | props }
-> ReactElement
unsafeCreateHookLeafElement k = createLeafElementImpl (unsafeCoerce k)
-- | Create an element using the [render props pattern](https://reactjs.org/docs/render-props.html#using-props-other-than-render) when the name of the render prop is "children".
createRenderPropsElement
:: forall required given childrenProps
. ReactPropFields required given
=> ReactClass { children :: childrenProps -> ReactElement | required }
-> { | given }
-> (childrenProps -> ReactElement)
-> ReactElement
createRenderPropsElement = createElementImpl