Как разделить магазин между несколькими компонентами с 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>