Closed laszlokorte closed 8 years ago
Sure this could be done but I am not sure why you would want this behavior. Can you elaborate with a use case?
Hm.. maybe I am approaching it in a wrong way.
const counterValue = Storage.local
.getItem('myCount') // request the stored value from the storage
.map(parseInt) // convert it to an integer
.take(1); // take only one value from the stream to prevent an feedback cycle with the later stored values
const myCounter = counter({
DOM: DOM,
initial$: counterValue, // pass the loaded value as initial value into the counter component
});
const storageRequests$ = myCounter
.values$ // the stream of the current counter value
.map((val) => ({ // request to store the current value of the counter
key: 'myCount',
value: val,
}));
Now since no value is emitted anymore if no value is stored the counterValue
stream never get's started. This leads to the counter never being rendered (as it depends on an initial value).
To start the sequence an initial value has to be injected into the counterValue stream.
const counterValue = Storage.local
.getItem('myCount')
.map(parseInt)
.startWith(42) // injecting it before the take(1) call does not work because it would never take the stored value
.take(1);
const counterValue = Storage.local
.getItem('myCount')
.map(parseInt)
.take(1)
.startWith(42) // If injecting it after the take(1) does not work. I am not exactly sure why. I think the take(1) does not prevent the feedback cycle anymore
const counterValue = Storage.local
.getItem('myCount')
.map(parseInt)
.take(1)
.defaultIfEmpty(42) // does not work because if no completion or error signal is sent the stream is not recognized as empty
The following is working:
const storageRequests$ = myCounter
.values$
.startWith(20) // injecting the default value into the storage requests
.map((val) => ({
key: 'myCount',
value: val,
}));
But this is not necessary what I want. I do not want any value to be stored until the user interacts with the counter.
What I would like to to is: Load a value from the store. If no value is stored used a default value. If the value get's changed by a user interaction save the new value to the store. No value should be stored until the user interacts with the component.
Hmm this is interesting. As I understand this:
const counterValue = Storage.local
.getItem('myCount')
.map(parseInt)
.take(1)
.startWith(42)
Should totally work, it has the desired output if you just use Rx. Would be interesting to find out why this does not work for you. Maybe @staltz has an idea?
.take(1).startWith(42)
should work and should be the preferred way. Just debug why isn't it working.
I got it working now. .take(1).startWith(42)
is working indeed. The problem was a initial$.last()
call inside my counter component which was used to get only the latest initial value:
const model = (initial$, actions) => {
const initialValue$ = initial$.last(); // does not work since the the stream never terminates if no
const modifications$ = makeModification(actions);
const value$ = initialValue$.merge(
modifications$
).scan((prev, modFn) => modFn(prev));
return value$.map(
(value) => { return {value}; }
);
};
Great. Then I can close this issue, yes?
The behavior I proposed in #9 turned out be not as useful as I suspected. Using
defaultIfEmpty
does not work as I expected because the stream returned bygetItem
never terminates and is never "empty".Propably I would be best to be able to pass an optional default value as second argument to
getItem
which get's emitted if no value is stored.But as I am not very experienced in RX myself I think someone else should take a look at it before we change anything. Maybe there is another better solution.