映射到嵌套组件的 Elm 效果

Elm Effects Mapped to Nested Component

在这个example (RandomGifPair)中,NewGif对应的update实际上是如何连接到父组件触发后执行的RandomGif.update act model.left?似乎 RandomGif.update NewGif maybeUrl 需要在某处手动触发。更明确一点,RandomGifPair 触发其 Left 更新操作并通过手动调用 RandomGif 的更新函数取回模型/效果对。返回的效果通过Effects.map Left fx执行,然后继续到RandomGif

中的getRandomGif函数
getRandomGif : String -> Effects Action
getRandomGif topic =
  Http.get decodeUrl (randomUrl topic)
    |> Task.toMaybe
    |> Task.map NewGif
    |> Effects.task

据我所知,它将继续触发 NewGif 动作,由于 Effects.map,它现在也被标记为 Left。图片中我唯一缺少的部分是这个动作是如何保持在 RandomGif 的范围内的,以及与这个更新的 NewGif 案例相对应的动作实际上被触发了:

update : Action -> Model -> (Model, Effects Action)
update action model =
  case action of
    RequestMore ->
      (model, getRandomGif model.topic)

    NewGif maybeUrl ->
      ( Model model.topic (Maybe.withDefault model.gifUrl maybeUrl)
      , Effects.none
      )

当 Main.elm 只有 RandomGifPair 的更新功能,因此没有 NewGif 的情况。

我确定答案在于我缺少的端口、Effects.map、forwardTo 或任务的具体细节。

作为参考,here is an attempt to solve the problem in javascript 在 NewGif 的上层更新函数中包含一个条目,并在其中手动调用 RandomGif.update。可能不是尝试理解榆树的最佳方式...

所有操作都会进入您的 top-level 更新功能。无法将操作范围限定为 sub-update 函数 - 它们总是位于顶部。因此,大多数程序将手动将操作路由到较低的更新功能。这就是这里发生的事情。所有操作都必须进入 RandomGifPair.update,并且由该函数决定 a) 调用子函数和 b) 将结果存储在状态中的正确位置。它可能会非常麻烦。

Here's the specific point in RandomGifPair.update that does the routing

第 42 行说“哦,这是一个 Left 动作?给我里面的 act。现在你已经存储了你想要的 NewGifRequestMoreact 中,您知道它绑定到左侧。第 44 行调用较低的更新函数,它知道如何处理。第 46 行存储结果模型中的下部更新函数(通过使用新的左侧和 re-using 旧的右侧重新创建整个模型)。

这一切都被 Effects 周围的样板所掩盖。如果你能先理解动作是如何流动的,然后回过头来将同样的逻辑应用到效果上,我想这会变得更清楚。

我可能来晚了一点,但效果很容易映射。

取值如下:

type alias ChildModel = { number : Int }

type alias Model = { firstChild : ChildModel }


type ChildMsg = One | Two | Three

type Msg Nothing | ChildMsg1 ChildMsg


childUpdate : ChildMsg -> ChildModel -> (ChildModel, Cmd ChildMsg)
childUpdate msg model =
  case msg of
    One -> { model | number = model.number + 1 }
    Two -> { model | number = model.number + 2 }
    Three -> { model | number = model.number + 3 }

update : Msg -> Model -> (Model, Msg)
update msg model =
  case msg of
    Nothing -> (model, Cmd.none)
    ChildMsg1 mesg -> -- heres where the update is mapped
      let
        (childModel, childMsg) = childUpdate mesg model.firstChild
      in
        ( { model | firstChild = childModel }, Cmd.map ChildMsg1 childMsg)

Cmd.map 将顶级 Msg 映射到嵌套组件。想想蛇吃自己的尾巴。