在 Elm 中使用 Dict.update 和嵌套更新函数

Using Dict.update with nested update functions in Elm

我正在编写一个 Elm 应用程序,其中状态的主要部分处于 Dict 状态,Records 作为值。我有一个主状态模型的更新函数,以及 Dict 中各个记录的更新函数。有没有办法使用 Dict.update 和记录状态更新功能?

我遇到的问题是记录的状态更新函数 returns 通常更新函数 return:一个包含更新对象和任何触发命令的元组(例如 (newRecord, Cmd.none)).但是 Dict.update 函数需要接收一条记录和 return 一条记录(例如 newRecord),而不是带有记录和命令对象的元组。

有办法解决这个问题吗?现在我已经使用 Dict.getDict.insert 的组合让它工作,但这看起来很笨拙。

据我所知没有。我的一个项目中有这个辅助函数

updateDict : comparable -> msg -> Dict comparable b -> (msg -> b -> ( b, Cmd msg )) -> (comparable -> msg -> c) -> ( Dict comparable b, Cmd c )
updateDict uid act dict fn wrapper =
    case Dict.get uid dict |> Maybe.map (fn act) of
        Just ( m, e ) ->
            ( Dict.insert uid m dict
            , Cmd.map (wrapper uid) e
            )

        Nothing ->
            ( dict, Cmd.none )

不确定这是否是您要查找的内容,但如果您的模型中有嵌套 Dict 结构,如下所示:

type alias Model =
  { parentsAndChildren : Dict String (Dict String Int) }

那就没必要让childupdate输出一个Cmd。您的更新可能如下所示:

update : Msg -> Model -> Model
update msg model = 
  case msg of
    NewChild parentName childName age ->
      let
        newModel =
          { model
          | parentsAndChildren =
              model.parentsAndChildren
              |> Dict.update 
                   parentName 
                   (Maybe.map insertChild)
          }

      in
        (newModel, Cmd.none)

-- helper function to update a child Dict
insertChild: String -> Int -> Dict (String Int) -> Dict (String Int)
insertChild name age childDict =
  Dict.insert name age childDict

NEEDS 输出 Cmd 的唯一 update 函数是顶级组件中的更新函数。 所以你的 child update 函数不必输出 Cmd.

如果记录的更新函数总是return Cmd.none,你可以简化它并且return只更新模型。

如果您的应用程序体系结构不需要,则子模块不必遵循 ( Model, Cmd Msg ) 约定。

如果您确实需要从较低级别传递命令,也可以重新构建子模块的 update 函数以简化这些更新。

示例基于 example/http

这是一个示例,说明如何拆分更新,以便您可以在顶级 update 中重复使用相同的逻辑,而无需额外的技巧。

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  (updateModel msg model, updateCmd msg model)


updateCmd: Msg -> Model -> Cmd Msg
updateCmd msg model =
  case msg of
    MorePlease ->
      getRandomGif model.topic

    _ ->
      Cmd.none


updateModel: Msg -> Model -> Model
updateModel msg model =
  case msg of
    NewGif (Ok newUrl) ->
      Model model.topic newUrl

    _ ->
      model

如果您需要 updateCmd 中的更新模型,则只需传递它而不是当前模型,或者如果您愿意,甚至可以传递两者。

作为奖励,您可以完全省略 case 表达式中未使用的分支。

使用Dict.update

也可以在 Dict.update 中使用 updateModel 而无需检索记录并将其写回。

Dict.update childKey (Maybe.map (updateModel childMsg)) model