// ticker$ will update every 3s
// showHand$ will only triger after user click button
// I would like to take last ticker price as user order price when user click button
let lastPrice: number;
this.ticker$
// What I am doing now is preserve value to vairable here.
.do(ticker => lastPrice = ticker.closePrice)
.switchMap(() => this.showHand$)
.subscribe(showHand => {
// use value here
this.order.price = lastPrice;
this.order.amount = showHand.amount;
this.order.type = showHand.type;
this.submit();
});
Any segestion about how to prevser value and switch map together, without one line variable like above?
Results selector function is deprecated in version 6 will be removed in version 7.
From docs:
https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/migration.md#result-selectors
with resultSelector (v5.x)
source.pipe(
switchMap(fn1, fn2)
)
the same functionality without resultSelector, achieved with inner map
source.pipe(
switchMap((a, i) => fn1(a, i).pipe(
map((b, ii) => fn2(a, b, i, ii))
)
)
The behaviour you require is already possible with an overload of SwitchMap with a selectorFunc for the combination of every (outerValue,innerValue):
this.ticker$
.switchMap(
() => this.showHand$,
(tickerValue, switchMap) => tickerValue
)
.subscribe(showHand => { });
There is a little hack to achieve this - basically you have a whole new observable inside the switchmap, and this observable has access to the value passed into the switchmap function. You can use this value in an inner map to preserve the value.
this.ticker$
.switchMap(ticker => this.showHand$.pipe(map(hand => ({ ticker,hand }) ))
.subscribe( obj => {
// use value here
this.order.price = obj.ticker;
this.order.amount = obj.hand.amount;
this.order.type = obj.hand.type;
this.submit();
});
I think this is the operator
this.showHand$.take(1)
.withLatestFrom(this.ticker$)
.subscribe(([showHand, ticker]) => {
this.order.price = ticker.closePrice;
this.order.amount = showHand.amount;
this.order.type = showHand.type;
this.submit();
});
Note, take(1) will close subscription, but if you want the user to be able to press the button many times, save the subscription to a const and unsubscribe when finished.
Related
I have useState "setAnswers" (set) and "answers" (get) (answers is array with strings)
and click trigger:
onClick = () => {
setAnswers((prev) => [...prev, e])
setValue(questionKey, answers)
console.log(watch(questionKey))
}
but with ever click i got only previous value
In fact, your console.log is execute before the state finish to be calculated, if you put your console.log on an other place, normally, you find what you want.
Try it, and say me
Your console.log(watch(questionKey)) all time will go wrong on onChange.
you need use a useEffect to log or make anything else with state as base.
useEffect(() => {
console.log(watch(questionKey)
}, [questionKey]);
to more info you can read here:
https://dev.to/mpodlasin/react-useeffect-hook-explained-in-depth-on-a-simple-example-19ec
I think you are a little bit confused: watch function from useForm is used to
Watch specified inputs and return their values.
So console.log(watch(questionKey)) does make no sense.
watch should be used in this way:
React.useEffect(() => {
const subscription = watch((value, { name, type }) => console.log(value, name, type));
return () => subscription.unsubscribe();
}, [watch]);
You use a useEffect to subscribe/unsubscribe watch on some form fields, and then in component's body you could call:
const watchSomething = watch(<something>);
and, every time the field called <something> will change his value, watch will execute console.log(value, name, type).
If you want to print the very last value of a state variable, you should know that setValue, setAnswer are async functions and its absolutely not guaranteed that on next line you could log the last value.
To solve your problem you have 2 choices:
use watch correctly;
forget watch and use classic useEffect:
onClick = () => {
setAnswers((prev) => [...prev, e])
setValue(questionKey, answers)
}
useEffect(() => {
console.log(questionKey); //<-- here you will print the very last value of questionKey
}, [questionKey]);
Here a guide on how to use watch.
Im using the .filter() method on an object inside of a useEffect() method to filter out certain arrays out by name if they exist in a second object. I need to get the difference of arrays back into a useState() method. Im using the following and works outside the useEffect() method:
useEffect(() => {
getDBData().then( (r) => { setAnotherObj(r); });
getAPICall().then((r) => {
let result = r.filter(
(o1) => !anotherObj.filter((o2) => o1.name === o2.name)
);
setOption(result);
});
}, []);
Now that works outside of the useEffect method when I add it to an event like onClick, but not inside, it might work one time then it doesn't at all. What am I missing about the useEffect method that I need to know why the filtering isn't being done?
Replace second filter with find.
!anotherObj.filter(o2 => o1.name === o2.name) will always return false, be it has elements or not.
There is no dependency array in the in the useEffect, so whenever there is a state change, this useEffect gets triggered again.
Finding A-B, filter all the elements of A that are not in B.
r.filter(o1 => anotherObj.find(o2 => o1.name !== o2.name)); With this it removes all the elements that are common in A and B. And leaves out only elements in A.
Update as follows,
useEffect( () => {
getAPICall().then( (r) => {
const result = r.filter(o1 => anotherObj.find(o2 => o1.name !== o2.name));
setOption(result);
});
}, []);
I have a listener that sets the state if the value exists. It previously worked fine when I just had one value like so.
subscribe('feature-settings-updated', (evt, enabledFeatures) => {
setIsEnabled(enabledFeatures.includes('showEmployeesReport'));
});
Now I have a second value and rather than duplicating the code I want to do it so that it takes an array and if either one of the value exists then set it to true. Following is my attempt but it only enables it for just the one. Any ideas?
const features = ['showEmployeesReport', 'showCustomersReport'];
subscribe('feature-settings-updated', (evt, enabledFeatures) => {
setIsEnabled(
features.some(exp => enabledFeatures.includes(exp))
);
});
You seem to reference the wrong variable (experiments instead of features):
const features = ['showEmployeesReport', 'showCustomersReport'];
subscribe('feature-settings-updated', (evt, enabledFeatures) => {
setIsEnabled(
features.some(feature => enabledFeatures.includes(feature))
);
});
I have a list called filteredUserNames which contains a lot of users. Everytime I change a value on my form it starts a new filter of the data. I know that to delay the time so that not every character triggers a new filter I need to use debounce, but I'm not sure where to add it. Should it be inside the value changes subscription? Also what is the correct way to implement it?
I have
searchString = new BehaviorSubject("");
searchString$ = this.searchString.asObservable();
In Constructor
this.myForm = this.fb.group({
searchString: [""],
});
this.myForm.controls.searchString.valueChanges.subscribe((val) => {
// SHOULD THE DEBOUNCE GO HERE ?? //
this.searchString.next(val);
}
In ngOnInit
this.searchString$.subscribe((searchTerm) => {
console.log(this.userNames);
if (this.userNames !== undefined) {
this.filteredUserNames = this.userNames.filter(
(userName) =>
userName.searchTerms
.toLowerCase()
.indexOf(searchTerm.toLowerCase()) !== -1
);
};
});
try this and you can add distinctUntilChanged to ignore similar values and tap operator for your side effects which is in your case emitting a new value to your behaviorSubject
import { tap, distinctUntilChanged, debounceTime} from 'rxjs/operators';
...
this.myForm.controls.searchString.valueChanges.pipe(
debounceTime(400),
distinctUntilChanged(),
tap((val) => this.searchString.next(val))
).subscribe()
I am trying to use RxJS to poll for events. However, I only have access to one function, which is getEvent(). I can do 2 things with the getEvent function:
getEvent("latest") — this will give me the latest event object
getEvent(eventId) - I pass in an integer and it will give me the event object corresponding to the eventId.
Event IDs always increment from 0, but the problem is, if my polling interval isn't small enough, I might miss events.
For example, if I do a getEvent("latest") and I get an event that has an ID of 1, that's great. But if the next time I call it, I get an ID of 3, I know that I missed an event.
In this case, I want to use a higher-order observable to call getEvent(2) and getEvent(3) so that the consumer of the stream I am creating won't have to worry about missing an event.
Right now, all I have is something like this:
timer(0, 500).pipe(
concatMap(() => from(getEvent("latest"))
)
For some context, I'm working off of this blogpost: https://itnext.io/polling-using-rxjs-b56cd3531815
Using expand to recursively call GET fits here perfectly. Here is an example with DEMO:
const source = timer(0, 2000)
const _stream = new Subject();
const stream = _stream.asObservable();
const s1 = source.pipe(tap(random)).subscribe()
const sub = stream.pipe(
startWith(0),
pairwise(),
concatMap((v: Array<number>) => {
let missing = v[1] - v[0];
return missing ? getMissing(v[0], missing) : EMPTY
})
).subscribe(console.log)
function getMissing(start, count) {
return getById(start).pipe(
expand(id => getById(id+1)),
take(count)
)
}
// helper functions for DEMO
let i = 1;
function random() {. // THIS IS YOUR getEvent('latest')
if (i < 10) {
i+=2;
_stream.next(i
// (Math.floor(Math.random() * 8))
)
}
}
function getById(id) {. // THIS IS YOUR getEvent(eventId)
return of(id).pipe(delay(1000)) // delay to mimic network
}