在 CycleJS 中用 localStorage 的初始值折叠
Fold with initial value from localStorage in CycleJS
我正在使用 cycleJS 制作一个表情符号查看器应用程序,用户可以在其中单击任何表情符号以 add/remove 从他们的收藏夹列表中选择它。每次更改时,该列表也会保存到 localstorage
。我正在使用 xstream 通过 folding 点击流(在每次点击时添加或删除表情符号)构建列表:
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 pre-load 放入最喜欢的流中。从 localStorage
加载数组后,我尝试以我能想到的所有方式使用 favEmojis$
流对其进行 merge/concat。例如:
const storedEmojis$ = localStorage
.getItem("favEmojis")
.map(favEmojis => (favEmojis ? JSON.parse(favEmojis) : []))
.take(1);
const combinedFav$ = xs.merge(storedEmojis$, favEmojis$);
但这不起作用 - 来自 localstorage
的数组被折叠 clickFavEmoji
流覆盖。如果有人能指出正确的方向,我将不胜感激。
N.B。完整的代码很长,所以我只包含了看起来最相关的部分。
这里的问题是你有两个事实来源:
- 折叠中使用的值;
- 本地存储中的值。
这两个来源根本不相互依赖,因此您遇到了奇怪的行为。
一个可行的解决方案是从 clickFav$
和 storedEmojis$
中创建 reducer,将它们合并并折叠在一起。
这是它的样子:
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$
的代码,就会发现它非常杂乱无章。它没有特定的业务逻辑,它只是愚蠢地调用给定的 reducers。
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$)
}
}
我正在使用 cycleJS 制作一个表情符号查看器应用程序,用户可以在其中单击任何表情符号以 add/remove 从他们的收藏夹列表中选择它。每次更改时,该列表也会保存到 localstorage
。我正在使用 xstream 通过 folding 点击流(在每次点击时添加或删除表情符号)构建列表:
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 pre-load 放入最喜欢的流中。从 localStorage
加载数组后,我尝试以我能想到的所有方式使用 favEmojis$
流对其进行 merge/concat。例如:
const storedEmojis$ = localStorage
.getItem("favEmojis")
.map(favEmojis => (favEmojis ? JSON.parse(favEmojis) : []))
.take(1);
const combinedFav$ = xs.merge(storedEmojis$, favEmojis$);
但这不起作用 - 来自 localstorage
的数组被折叠 clickFavEmoji
流覆盖。如果有人能指出正确的方向,我将不胜感激。
N.B。完整的代码很长,所以我只包含了看起来最相关的部分。
这里的问题是你有两个事实来源:
- 折叠中使用的值;
- 本地存储中的值。
这两个来源根本不相互依赖,因此您遇到了奇怪的行为。
一个可行的解决方案是从 clickFav$
和 storedEmojis$
中创建 reducer,将它们合并并折叠在一起。
这是它的样子:
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$
的代码,就会发现它非常杂乱无章。它没有特定的业务逻辑,它只是愚蠢地调用给定的 reducers。
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$)
}
}