Elm - 将 Msg 转换为 Cmd Msg

20
我试图修改一个简单的应用程序,使其首先更新模型,然后触发另一个更新。请参考elm-lang教程
update msg model =
  case msg of
    MorePlease ->
      (model, getRandomGif model.topic)

    NewGif (Ok newUrl) ->
      ( { model | gifUrl = newUrl }, Cmd.none)

    NewGif (Err _) ->
      (model, Cmd.none)

    -- my addition
    NewTopic newTopic ->
      ({ model | topic = newTopic}, MorePlease)

这个编译器失败是因为NewTopic分支:

The 3rd branch has this type:

( { gifUrl : String, topic : String }, Cmd Msg )

But the 4th is:

( { gifUrl : String, topic : String }, Msg )

因此,我的 Msg 需要是类型 Cmd Msg。我该如何将“我的 Msg”转换为 Cmd Msg?

注意:我认识到有一种更简单的方法来进行这种更改,但我正在尝试更深入地了解 Elm。

2个回答

41

没有必要将Msg转换为Cmd Msg。请记住,update只是一个函数,因此您可以递归调用它。

您的NewTopic case处理程序可以简化为以下内容:

NewTopic newTopic ->
    update MorePlease { model | topic = newTopic}

如果您真的希望在这种情况下触发Elm架构中的Cmd,您可以简单地将Cmd.none映射到所需的Msg:

NewTopic newTopic ->
    ({ model | topic = newTopic}, Cmd.map (always MorePlease) Cmd.none)

(实际上不推荐使用)


谢谢。这是解决问题的更简单的方法。我仍然想知道,我是否需要将 Msg 转换为 Cmd Msg?如果需要,我该如何转换? - steel
1
返回一个 Cmd 将会导致 Elm 架构生命周期的另一个循环,所以如果只是为了触发另一个 Msg,你实际上只是浪费了额外的 CPU 循环。可能有一些原因想要强制进行另一个事件循环(比如动画或其他视图更新,但即使这些也需要额外的代码)。在我看来,大多数情况下,强制执行不必要的 Cmd 感觉就像是忽略了 update 函数作为函数的本质,并且误解了事件循环。 - Chad Gilbert
有没有可能在“MorePlease”消息中传递一些参数? - Oskar Szura
2
是的,任何导致 Msg 的结果都可以使用。例如:Cmd.map (always (SomethingElse param1 param2)) Cmd.none - Chad Gilbert
我曾经也遇到过类似的问题,理解起来基本相同。我将更新分成多个函数,并根据联合类型选择它们。一旦用户登录,类型就会改变,然后将使用新的更新函数。我有一些可以刷新的数据,希望在登录时自动刷新,因此我试图通过事件系统发送“刷新”消息。实际上,我真正需要做的是在另一个更新函数中使用生成命令的函数,我已经很好地将其命名为“refresh”,但没有意识到我可以用它来刷新状态。 - James Andariese
显示剩余2条评论

15

添加以下函数:

run : msg -> Cmd msg
run m =
    Task.perform (always m) (Task.succeed ())

你的代码将变成:

NewTopic newTopic ->
      ({ model | topic = newTopic}, run MorePlease)

1
提供你的答案解释。 - Narendra Jadhav
2
我的消息有一个参数,所以语法不同,但是当采用被接受的答案中所示的Cmd.map无效时,使用Task.perform对我很有效。 - CoderDennis
工作得很好(elm 0.19.1)!很好的辅助函数,使代码比内联更清晰。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接