Сгиб с начальным значением из localStorage в CycleJS

Я делаю приложение для просмотра смайликов с помощью cycleJS, где пользователь может щелкнуть любой смайлик, чтобы добавить / удалить его из списка избранных. Список также сохраняется в localstorage на каждое изменение. Я строю список, используя xstream, сворачивая поток кликов (добавляя или удаляя эмодзи при каждом клике):

const favEmojis$ = clickFavEmoji$.fold(
  (favList, selectedEmoji) =>
    favList.includes(selectedEmoji)
      ? favList.filter(emoji => emoji !== selectedEmoji)
      : [...favList, selectedEmoji],
    []
);

Я могу сохранить этот поток в localStorage и загрузите его на страницу, используя драйвер @ cycle/ storage:

const storageRequest$ = favEmojis$.map(favEmojis => ({
    key: "favEmojis",
    value: JSON.stringify(favEmojis)
  }));
...
return {
    DOM: vdom$,
    ...
    storage: storageRequest$
  };
}

Однако я не могу понять, как предварительно загрузить массив из localStorage в любимый поток. Как только массив загружен из localStorage Я попытался объединить / согласовать его с favEmojis$ поток во всех отношениях, о которых я мог думать. Например:

const storedEmojis$ = localStorage
    .getItem("favEmojis")
    .map(favEmojis => (favEmojis ? JSON.parse(favEmojis) : []))
    .take(1);

const combinedFav$ = xs.merge(storedEmojis$, favEmojis$);

Но это не работает - массив из localstorage перезаписывается при сворачивании clickFavEmoji поток. Я был бы очень признателен, если бы кто-то указал мне правильное направление.

Примечание: полный код довольно длинный, поэтому я включил только те части, которые казались наиболее актуальными.

1 ответ

Решение

Проблема в том, что у вас есть два источника правды:

  • значение, используемое в сгибе;
  • значение в местном хранилище.

Оба источника вообще не зависят друг от друга, отсюда и странное поведение, которое вы испытываете.

Решением, которое могло бы работать, было бы создание редукторов от вас обоих clickFav$ а также storedEmojis$, объединить и сложить их все вместе.

Вот как это будет выглядеть:

const clickReducer$ = clickFavEmoji$.map(
  (favEmojis, selected) => /* same as you previous reducer */
);

const storedEmojisReducer$ = localStorage
  .getItem("favEmojis")
  .take(1)
  .map(/* serialise to json */)
  .map((state, favEmojis) => favEmojis) // here we just replace the whole state

const favEmojis$ = xs
  .merge(storedEmojisReducer$, clickReducer$)
  .fold(
    (favEmojis, reducer) => reducer(favEmojis)
  , [])

return {
  DOM: favEmojis$.map(render)
}

Таким образом, существует явная связь между значением в localStorage и значением, которое развивается в течение жизненного цикла приложения.

С onionify

Теперь предыдущее решение работает хорошо. Когда редуктор вызывается, он знает о предыдущем значении, заданном localStorage. Но если вы посмотрите ближе на код, который создает favEmojis$Это довольно много шума. У него нет конкретной бизнес-логики, он просто тупо называет данные редукторы.

onionify ( https://github.com/staltz/cycle-onionify) значительно упрощает процесс управления состоянием в циклическом приложении, централизуя все вызовы редукторов в одной точке и повторно внедряя новое состояние в источники вашего приложения.

Код не сильно изменится по сравнению с предыдущей версией, изменения будут: - состояние будет введено как явная зависимость вашего компонента; - вам не придется вручную вызывать редукторы.

function Component({ DOM, onion /* ... */ }) {
  const clickReducer$ = /* same as before */

  const storedEmojisReducer$ = /* same as before */

  return {
    DOM: onion
      .state$ // the state is now inside onionify
      .map(render),

    // send the reducers to onionify
    onion: xs.merge(storedEmojisReducer$, clickReducer$)
  }
}
Другие вопросы по тегам