Как разделить магазин между несколькими компонентами с Elm?

У меня есть статическая страница с двумя компонентами.

  • Один в заголовке, который показывает меню, обрабатывающее пользовательские настройки / логин и регистрацию
  • Один на главной странице, который может отображать список изображений пользователя или форму, например, о профиле пользователя.

Есть ли способ поделиться состоянием (скажем, access_token для API, например) между этими двумя компонентами?

Я мог бы добавить порты для каждого компонента, которые обновляли бы ключ localStorage и отправляли хранилище другим компонентам.

Но есть ли лучший способ для этого?

1 ответ

В итоге я решил использовать два порта:

port saveStore : String -> Cmd msg

port storeChanged : (String -> msg) -> Sub msg

С декодерами и кодерами:

serializeStore : AccessToken -> Profile -> Cmd Msg
serializeStore access_token profile =
    encodeStore access_token profile
        |> saveStore


encodeStore : AccessToken -> Profile -> String
encodeStore access_token profile =
    let
        encoded =
            Encode.object
                [ ( "access_token", tokenToString access_token |> Encode.string )
                , ( "profile", encodeUserProfile profile )
                ]
    in
    Encode.encode 0 encoded


deserializeStore : String -> Maybe Store
deserializeStore =
    Decode.decodeString decodeStore >> Result.toMaybe


decodeStore : Decoder Store
decodeStore =
    Decode.map2 Store
        decodeToken
        (Decode.field "profile" decodeProfile)


decodeToken : Decoder AccessToken
decodeToken =
    Decode.field "access_token" Decode.string
        |> Decode.andThen
            (\token ->
                stringToToken token
                    |> Decode.succeed
            )

И затем я использую их для синхронизации моего хранилища компонентов и сохраняю копию в localStorage:

  <script src="app.js"></script>
  <script>
    // The localStorage key to use to store serialized session data
    const storeKey = "store";

    const headerElement = document.getElementById("header-app");
    const headerApp = Elm.Header.init({
      node: element,
      flags: {
        rawStore: localStorage[storeKey] || ""
      }
    });

    const contentElement = document.getElementById("content-app");
    const contentApp = Elm.Content.init({
      node: element,
      flags: {
        rawStore: localStorage[storeKey] || ""
      }
    });

    headerApp.ports.saveStore.subscribe((rawStore) => {
      localStorage[storeKey] = rawStore;
      contentApp.ports.storeChanged.send(rawStore);
    });

    contentApp.ports.saveStore.subscribe((rawStore) => {
      localStorage[storeKey] = rawStore;
      headerApp.ports.storeChanged.send(rawStore);
    });

    // Ensure session is refreshed when it changes in another tab/window
    window.addEventListener("storage", (event) => {
      if (event.storageArea === localStorage && event.key === storeKey) {
        headerApp.ports.storeChanged.send(event.newValue);
        contentApp.ports.storeChanged.send(event.newValue);
      }
    }, false);
  </script>

Другие вопросы по тегам