为什么有时只能使用{}的ReactClass道具?

时间:2016-10-17 17:16:43

标签: purescript

以下(凌乱,可能不连贯)Purescript代码是一个玩具Thermite / Websocket脚本,当按下按钮时,它会将一些文本发送到Websocket:

module Main where

import Prelude
import React as R
import React.DOM.Props as RP
import ReactDOM as RDOM
import Thermite as T
import Control.Coroutine (cotransform, connect, runProcess, Producer, Consumer, consumer, producer, emit, ($$))
import Control.Coroutine.Aff (produce', produceAff, produce)
import Control.Monad.Aff (launchAff, runAff, Aff)
import Control.Monad.Aff.AVar (AVAR)
import Control.Monad.Aff.Class (liftAff)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Class (liftEff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Control.Monad.Eff.Exception (EXCEPTION)
import Control.Monad.Eff.Var (($=))
import Control.Monad.Writer (lift)
import DOM (DOM) as DOM
import DOM.HTML (window) as DOM
import DOM.HTML.HTMLAnchorElement (download)
import DOM.HTML.Types (htmlDocumentToParentNode) as DOM
import DOM.HTML.Window (document) as DOM
import DOM.Node.ParentNode (querySelector) as DOM
import Data.Either (Either(..))
import Data.Generic (class Generic, gShow)
import Data.Maybe (fromJust, Maybe(..))
import Data.Nullable (toMaybe)
import Partial.Unsafe (unsafePartial)
import React.DOM (text, p', td', input, tr', tbody', th, thead', table, div, h1', button) as R
import Unsafe.Coerce (unsafeCoerce)
import WebSocket (WEBSOCKET, Connection(..), Message(..), URL(..), runMessageEvent, runMessage, newWebSocket)

data Action = SendButtonPress | SetText String

derive instance genericAction :: Generic Action

instance showAction :: Show Action where
  show = gShow

type State = { connection :: Connection
             , someText :: String
             }

initialState :: Connection -> State
initialState socket = { connection: socket
                      , someText: ""
                      }

render :: T.Render State _ Action
render dispatch _ state _ =
  [ R.p' [ R.text "Value: "
         , R.text $ show state.someText
         ]
  , R.p' [ R.input [ RP.placeholder "Enter rubbish here"
                   , RP.onChange \e -> dispatch (SetText (unsafeCoerce e).target.value)
                   ] []
         , R.button [ RP.onClick \_ -> dispatch SendButtonPress ]
                    [ R.text "Send"]
         ]
  ]

performAction :: T.PerformAction _ State _ Action
performAction SendButtonPress _ state        = void $ lift (sendMsg state.someText state.connection)
performAction (SetText s) _ _ = void $ T.cotransform $ _ { someText = s}

sendMsg :: forall a eff. (Show a) => a -> Connection -> Aff (ws :: WEBSOCKET, err :: EXCEPTION | eff) Unit
sendMsg m (Connection s) = liftEff $ s.send (Message (show m))

spec :: T.Spec _ State _ Action
spec = T.simpleSpec performAction render

wsProducer :: forall eff. Connection -> Producer String (Aff (avar :: AVAR, err :: EXCEPTION, ws :: WEBSOCKET | eff)) Unit
wsProducer (Connection s) = produce \emit -> do
  s.onmessage $= \event -> do
    emit $ Left $ runMessage (runMessageEvent event)

wsConsumer :: forall t22 eff t31. (Show t31) => Consumer t31 (Aff ( console :: CONSOLE | eff ) ) t22
wsConsumer = consumer \msg -> do
  liftEff $ log $ show msg
  pure Nothing

main :: Aff (avar :: AVAR, dom :: DOM.DOM, err :: EXCEPTION, ws :: WEBSOCKET, console :: CONSOLE) Unit
main = do
  socket <- liftEff $ newWebSocket (URL "ws://echo.websocket.org") []
  let state = initialState socket
  component <- liftEff (gitItInTheDOM state)
  runProcess (connect (wsProducer socket) (wsConsumer))

gitItInTheDOM :: forall eff props. State -> (Eff (dom :: DOM.DOM | eff) (R.ReactClass props))
gitItInTheDOM state = do
  document <- DOM.window >>= DOM.document
  let aJarOfSomeSort = DOM.querySelector "#container" (DOM.htmlDocumentToParentNode document)
  container <- unsafePartial (fromJust <<< toMaybe <$> aJarOfSomeSort)
  let component = T.createClass spec state
  -- RDOM.render (R.createFactory component {}) container
  pure component

我的问题是关于gitItInTheDOM,当RDOM.render行被取消注释时,代码不再进行类型检查。这是错误消息:

Could not match type

    {}                                                                                                          

  with type                                                                                                     

    props1                                                                                                      


while trying to match type ReactClass {}                                                                        
  with type ReactClass props1                                                                                   
while checking that expression (bind ((bind window) document)) (\$4 ->                                          
                                                                  case $4 of                                    
                                                                    document -> ...                             
                                                               )                                                
  has type Eff                                                                                                  
             ( dom :: DOM                                                                                       
             | eff0                                                                                             
             )                                                                                                  
             (ReactClass props1)                                                                                
in value declaration gitItInTheDOM                                                                              

where props1 is a rigid type variable                                                                           
        bound at line 91, column 1 - line 97, column 8                                                          
      eff0 is a rigid type variable                                                                             
        bound at line 91, column 1 - line 97, column 8

gitItInTheDOM更改为此类型会检查:

gitItInTheDOM :: forall eff. State -> Eff (dom :: DOM.DOM | eff) Unit
gitItInTheDOM state = void do
  document <- DOM.window >>= DOM.document
  let aJarOfSomeSort = DOM.querySelector "#container" (DOM.htmlDocumentToParentNode document)
  container <- unsafePartial (fromJust <<< toMaybe <$> aJarOfSomeSort)
  let component = T.createClass spec state
  RDOM.render (R.createFactory component {}) container

但是不返回组件,它如何返回组件并仍然键入检查,好吗?在某些情况下,类型检查器似乎只接受props {} createFactory的{​​{1}}参数,我不明白这些情况是什么,以及为什么它不是类型检查。

尝试组件的想法是将“驱动程序”传递回main,然后将其传递给wsConsumer,从而可以更新DOM(使用Thermite / React)通过WebSocket接收新消息。我也不知道这是否是实现我正在尝试做的有效方法,如果这没有意义:它怎么能有效?

1 个答案:

答案 0 :(得分:2)

如果您将原始类型签名更改为

gitItInTheDOM :: forall eff. State -> (Eff (dom :: DOM.DOM | eff) (R.ReactClass _))

这应该是类型检查,并告诉你通配符缺少的类型。

为什么?

这是关于普遍量化的。您的原始类型包含forall props,因此对于我们选择为props实例化的任何类型,gitItInTheDOM的定义应该是有意义的。事实上,从T.createClass返回的组件可以为ReactClass props提供props类型,因为您的规范未使用render或{{}中的道具1}},所以这没关系 - 您可以通过编写performAction的类型明确指导编译器关于spec的投诉来看到这一点。

但是,当您取消注释呈现组件的行时,_已提供值R.createFactory,将{}修复为特定类型props,与您的内容冲突声称该功能适用​​于{}任何类型。

所以你可以通过修复返回类型(... props)或允许调用者传入他们选择的道具来解决这个问题,不管是什么类型的道具。