为什么有时只有 {} 的 React 类 props 是可以的?
Why is it okay to have a ReactClass props of {} only sometimes?
以下(杂乱无章,可能语无伦次)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 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
但是没有return组件,请问如何return组件还能进行类型检查?似乎类型检查器在某些情况下只会接受 {}
的 props
参数用于 createFactory
,我不明白这些情况是什么,以及为什么不进行类型检查。
puring组件的想法是将'driver'传递回main
,然后可以将其传递给wsConsumer
,从而可以更新DOM(使用 Thermite/React)当通过 WebSocket 接收到新消息时。我也不知道这是否是实现我想要做的事情的有效方法,如果这没有意义:如何使其有效?
如果您将原始类型签名更改为
gitItInTheDOM :: forall eff. State -> (Eff (dom :: DOM.DOM | eff) (R.ReactClass _))
这应该进行类型检查,并告诉您通配符缺少的类型。
为什么?
这是关于通用量化的。您的原始类型包含一个 forall props
,因此对于我们选择为 props
实例化的任何类型,gitItInTheDOM
的定义应该是有意义的。事实上,你的组件 return 从 T.createClass
编辑可以为任何 props
指定 ReactClass props
类型,因为你的规范没有使用 render
中的道具或 performAction
,所以这没关系 - 您可以通过编译器关于 _
.
的抱怨明确指导写出 spec
的类型来看到这一点
然而,当您取消注释呈现组件的行时,R.createFactory
已提供值 {}
,将 props
固定为特定类型 {}
,冲突你声称该函数适用于 any 输入 props
.
因此,您可以通过修复 return 类型 (...R.ReactClass {}
) 或允许调用者传递他们选择的道具来解决此问题,无论是什么类型。
以下(杂乱无章,可能语无伦次)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 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
但是没有return组件,请问如何return组件还能进行类型检查?似乎类型检查器在某些情况下只会接受 {}
的 props
参数用于 createFactory
,我不明白这些情况是什么,以及为什么不进行类型检查。
puring组件的想法是将'driver'传递回main
,然后可以将其传递给wsConsumer
,从而可以更新DOM(使用 Thermite/React)当通过 WebSocket 接收到新消息时。我也不知道这是否是实现我想要做的事情的有效方法,如果这没有意义:如何使其有效?
如果您将原始类型签名更改为
gitItInTheDOM :: forall eff. State -> (Eff (dom :: DOM.DOM | eff) (R.ReactClass _))
这应该进行类型检查,并告诉您通配符缺少的类型。
为什么?
这是关于通用量化的。您的原始类型包含一个 forall props
,因此对于我们选择为 props
实例化的任何类型,gitItInTheDOM
的定义应该是有意义的。事实上,你的组件 return 从 T.createClass
编辑可以为任何 props
指定 ReactClass props
类型,因为你的规范没有使用 render
中的道具或 performAction
,所以这没关系 - 您可以通过编译器关于 _
.
spec
的类型来看到这一点
然而,当您取消注释呈现组件的行时,R.createFactory
已提供值 {}
,将 props
固定为特定类型 {}
,冲突你声称该函数适用于 any 输入 props
.
因此,您可以通过修复 return 类型 (...R.ReactClass {}
) 或允许调用者传递他们选择的道具来解决此问题,无论是什么类型。