3

我正在使用 cycleJS 制作一个表情符号查看器应用程序,用户可以在其中单击任何表情符号以从他们的收藏夹列表中添加/删除它。该列表也会localstorage在每次更改时保存。我通过折叠点击流(在每次点击时添加或删除表情符号)使用xstream构建列表:

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

我可以使用@cycle/storage驱动程序将此流保存localStorage并加载到页面上:

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$);

但这不起作用 - 数组 fromlocalstorage被折叠clickFavEmoji流覆盖。如果有人能指出我正确的方向,我将不胜感激。

注意完整的代码很长,所以我只包括了看起来最相关的部分。

4

1 回答 1

2

这里的问题是你有两个事实来源:

  • 折叠中使用的值;
  • 本地存储中的值。

这两个来源根本不相互依赖,因此您正在经历奇怪的行为。

一个可行的解决方案是从你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

现在,以前的解决方案效果很好。当调用 reducer 时,它会知道 localStorage 给出的先前值。但是,如果您仔细查看创建favEmojis$. 它没有特定的业务逻辑,只是愚蠢地调用给定的 reducer。

onionify( https://github.com/staltz/cycle-onionify ) 通过将所有对 reducer 的调用集中在一个点并将新状态重新注入到应用程序的源中,极大地简化了循环应用程序中管理状态的过程。

代码与以前的版本相比不会有太大变化,变化是: - 状态将作为组件的显式依赖项注入;- 您不必手动调用减速器。

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$)
  }
}
于 2017-12-25T10:05:57.240 回答