@@ -255,31 +255,38 @@ withTrace def = def { update = tracingUpdate, view = tracingView }
255
255
-- | "renders" it as a React DOM element, suitable for passing to
256
256
-- | `ReactDOM.render` or embedding in a JSX DOM tree.
257
257
bindComponent :: ∀ msg state
258
- . BaseComponent -- ^ A JS class inheriting from React.Component to serve as base
259
- -> ComponentDef msg state -- ^ The component definition
258
+ . BaseComponent state msg -- ^ A JS class inheriting from React.Component to serve as base
260
259
-> StateStrategy state -- ^ Strategy of storing state
260
+ -> ComponentDef msg state -- ^ The component definition
261
261
-> ReactElement
262
- bindComponent cmpt def stateStrategy =
262
+ bindComponent cmpt stateStrategy = \def -> -- Explicit lambda to make sure `def` isn't captured by closures under `where`
263
263
runFn2 instantiateBaseComponent cmpt
264
- { init: initialize
264
+ { def
265
+ , init: let (Transition s _) = def.init in (stateStrategy { initialState: s }).initialize
265
266
, render
266
- , componentDidMount: runCmds initialCmds
267
+ , componentDidMount: let ( Transition _ cmds) = def.init in runCmds cmds
267
268
, componentWillUnmount: setUnmounted true <> stopSubscriptions
268
269
}
269
270
where
270
- Transition initialState initialCmds = def.init
271
+ getState component = do
272
+ Transition s _ <- instancePropDef component <#> _.init
273
+ (stateStrategy { initialState: s }).getState component
271
274
272
- { initialize, getState, setState } = stateStrategy { initialState }
275
+ setState component newState callback = do
276
+ Transition s _ <- instancePropDef component <#> _.init
277
+ (stateStrategy { initialState: s }).setState component newState callback
273
278
274
279
render :: ReactComponentInstance -> Effect ReactElement
275
280
render component = do
276
281
state <- getState component
277
- pure $ def.view state $ dispatchMsg component
282
+ view <- instancePropDef component <#> _.view
283
+ pure $ view state $ dispatchMsg component
278
284
279
285
dispatchMsg :: ReactComponentInstance -> Dispatch msg
280
286
dispatchMsg component msg = unlessM (getUnmounted component) do
281
287
oldState <- getState component
282
- let Transition newState cmds = def.update oldState msg
288
+ update <- instancePropDef component <#> _.update
289
+ let Transition newState cmds = update oldState msg
283
290
setState component newState $ runCmds cmds component
284
291
285
292
runCmds :: Array (Command Aff msg ) -> ReactComponentInstance -> Effect Unit
@@ -320,7 +327,7 @@ construct :: ∀ msg state
320
327
construct def = do
321
328
stateStorage <- liftEffect dedicatedStorage
322
329
pure $ withFreshComponent \cmpt ->
323
- bindComponent cmpt def stateStorage
330
+ bindComponent cmpt stateStorage def
324
331
325
332
-- | Monad transformation applied to `ComponentDef'`
326
333
nat :: ∀ m n msg state . (m ~> n ) -> ComponentDef' m msg state -> ComponentDef' n msg state
@@ -362,8 +369,8 @@ wrapWithLocalState :: ∀ msg state args
362
369
-> args
363
370
-> ReactElement
364
371
wrapWithLocalState name mkDef =
365
- runFn2 withCachedComponent name \cmpt args ->
366
- bindComponent cmpt (mkDef args) localState
372
+ runFn2 withCachedComponent name \cmpt ->
373
+ bindComponent cmpt localState <<< mkDef
367
374
368
375
-- | A unique name for a component created via `wrapWithLocalState`. These names
369
376
-- | don't technically need to be _completely_ unique, but they do need to be
@@ -397,14 +404,15 @@ newtype ComponentName = ComponentName String
397
404
398
405
-- Props for the React component that is used as base for this framework. The
399
406
-- component itself is defined in the foreign module.
400
- type BaseComponentProps =
401
- { init :: ReactComponentInstance -> Effect Unit
407
+ type BaseComponentProps state msg =
408
+ { def :: ComponentDef msg state
409
+ , init :: ReactComponentInstance -> Effect Unit
402
410
, render :: ReactComponentInstance -> Effect ReactElement
403
411
, componentDidMount :: ReactComponentInstance -> Effect Unit
404
412
, componentWillUnmount :: ReactComponentInstance -> Effect Unit
405
413
}
406
414
407
- type BaseComponent = ReactComponent BaseComponentProps
415
+ type BaseComponent state msg = ReactComponent ( BaseComponentProps state msg )
408
416
409
417
-- This is just a call to `React.createElement`, but we can't use the
410
418
-- general-purpose `createElement` function from `./React.purs`, because it
@@ -413,7 +421,7 @@ type BaseComponent = ReactComponent BaseComponentProps
413
421
-- possible to make this type passable to JS by using `Foreign` and maybe even
414
422
-- `unsafeCoerce` in places, but I have decided it wasn't worth it, because this
415
423
-- is just one place at the core of the framework.
416
- foreign import instantiateBaseComponent :: Fn2 BaseComponent BaseComponentProps ReactElement
424
+ foreign import instantiateBaseComponent :: ∀ state msg . Fn2 ( BaseComponent state msg ) ( BaseComponentProps state msg ) ReactElement
417
425
418
426
-- | On first call with a given name, this function returns a fresh React class.
419
427
-- | On subsequent calls with the same name, it returns the same class. It has
@@ -423,9 +431,12 @@ foreign import instantiateBaseComponent :: Fn2 BaseComponent BaseComponentProps
423
431
-- This is essentially a hack, but not quite. It operates in the grey area
424
432
-- between PureScript and JavaScript. See comments on `ComponentName` for a more
425
433
-- detailed explanation.
426
- foreign import withCachedComponent :: ∀ a . Fn2 ComponentName (BaseComponent -> a ) a
434
+ foreign import withCachedComponent :: ∀ a state msg . Fn2 ComponentName (BaseComponent state msg -> a ) a
427
435
428
436
-- | Creates a fresh React component on every call. This is similar to
429
437
-- | `withCachedComponent`, but without the cache - creates a new component
430
438
-- | every time.
431
- foreign import withFreshComponent :: ∀ a . (BaseComponent -> a ) -> a
439
+ foreign import withFreshComponent :: ∀ a state msg . (BaseComponent state msg -> a ) -> a
440
+
441
+ -- Retrieves the `this.props.def` from the given component
442
+ foreign import instancePropDef :: ∀ state msg . ReactComponentInstance -> Effect (ComponentDef msg state )
0 commit comments