Ngrx waiting for Multiple API calls in effect - api

I'm super new about ngrx and I'm trying to develop an effect to dispatch multiple calls to my API to retrive a child list of objects.
Here my code.
loadMyChildren$ = createEffect (() => this.actions$.pipe(
ofType(SomeActions.loadMyChildren),
switchMap(({parentsObjArr}) => {
const obsList$: Observable<ChildModel>[] = parentsObjArr.result.map(parentsObj => this.childrenService.loadTimeSeries(parentsObj));
let childrenArr: ChildModel[] = [];
const source$ = zip(obsList$);
source$.subscribe((res) =>{
childrenArr = res;
}, err => { console.log(err); });
console.log('childrenArr', childrenArr)
return [
SomeActions.loadChildrenSuccess({childrenArr}),
SomeActionsTwo.loadParentsSuccess({parentsObjArr})
]
}),
catchError((err) => {
return of(SomeActions.loadMyChildrenFailed)
})
)
Unfortunately my effect seem doesn't wait for my API requests complete on "zip" (I tried also combineLatest and forkJoin) before dispatching the actions on the return array... "console.log" of my objects array is always empty and the store, consequently, does not keep any data.
What I'm wrong?
rxjs 6.6.0
angular 13.2.3

It's not good practise to manualy subscribe inside switchMap.
You are right, your code returns action before your API requests completes, you need to manipultate the stream without subscribing inside.
I would refactor your coude, so source$ is returned in switchMap, but its result is maped to desired ngrx actions with props that you wanted.
It would looked something like that:
switchMap(({parentsObjArr}) =>
zip(parentsObjArr.result.map(parentsObj => this.childrenService.loadTimeSeries(parentsObj)))
.pipe(
map(child => [
SomeActions.loadChildrenSuccess({child}),
SomeActionsTwo.loadParentsSuccess({parentsObjArr})
]
)
)
)

Related

React-Native-BLE-plx : how to get all scanned device list of advertising ble devices using ble-plx or BLE-Manager

im making a ble devices scanning app and im facing 2 different problems with these two libraries.
using ble-plx: it is only returning a single device object after scanning not a list of device.
.
with ble-plx i tried this code.
bleManager.startDeviceScan(
[],
{allowDuplicates: false},
(error, device) => {
if (error) {
console.log('ERRO: ', error);
return;
} else {
deviceList.push(device);
console.log(deviceList);
settotalDevices(deviceList.map(item => item.id));
console.log(deviceList.map(item => item.id));
// if (device.localName || device.name === 'POCO M3') {
// device
// ?.connect()
// .then(() => {
// console.log('connected to', device?.name);
// return device?.discoverAllServicesAndCharacteristics;
// })
// .catch(err => console.log('Error: ', err));
}
bleManager.stopDeviceScan();
I trid to map the returned object in a list hopeing that it will store all the device objects during scan but it only recives 1 device in the object.Although if i give a device name and perform connect it gets that specific device and connects to it but i need a list of all devices.
with BLE-Manager I tried:
to check whether stopScan is not called too early I Placed it in a setTimeout but the getDiscoveredPeripherals() consoles its output before the stop scan is called because of the setTimeout. and when I click on scan button consecutively it scans and returns number of discovered peripherals 0 or 1 or 2 or 3.
here is the code.
await BleManager.start({showAlert: false, forceLegacy: true}).then(() => {
// Success code
console.log('Module initialized');
});
await BleManager.scan([], 10, true, {
matchMode: 1,
scanMode: 2,
})
.then(Results => {
// Success code
console.log('Scan started');
})
.catch(error => console.log(error));
setTimeout(() => {
BleManager.stopScan().then(() => {
console.log('Scan stopped');
});
}, 11000);
await BleManager.getDiscoveredPeripherals().then(peripheralsArray => {
// Success code
settotalDevices(peripheralsArray.length);
console.log('Discovered peripherals: ' + peripheralsArray.length);
console.log(peripheralsArray);
deviceList = peripheralsArray.map(item => item.id);
console.log(deviceList);
});
i think the scan callback is being instantiated multiple times as the scan is not completed when device list is returned.
I would appriciate if anyone can solve my problem using any of the two libraries because it is very hard to find an implementation example for this in a react native functional commponent.
It was a logical error the getDiscoveredPeripherals() method was placed outside the stopScan() method. The documentation is not that well written for ble libs so it was by chance that i tried to put my discoveredperipherals method inside the stop method so it returned results after the execution of stopScan(). and i was tring to do it via async await but async await just executes a method whenever it finishes executing it.
sometime the slightest mistake can cause chaotic results.
hope someone in need finds this cause I didn't and it cost time.

Vuex Object Shows Null in Vue Component

In the app I'm creating I have a base store for objects that might be used across the entire app, such as the logged in user, validation errors and the like.
I also have other namespaced modules for specific sections of my app.
When my parent component is loaded there is an ajax call that pulls in data and commits it to the various stores.
export const instantiate = ({ commit, dispatch }) => {
return axios.get('/setup/fetch')
.then((response) => {
dispatch('base/setLoggedInUser', response.data.user, { root: true })
commit('setFetishesList', response.data.fetishes)
commit('setColorsList', response.data.colors)
commit('setRolesList', response.data.roles)
commit('setGendersList', response.data.genders)
commit('setOrientationsList', response.data.orientations)
commit('setLookingsList', response.data.lookings)
commit('setSeekingsList', response.data.seekings)
commit('setBodiesList', response.data.bodies)
commit('setHeightsList', response.data.heights)
commit('setEthnicitiesList', response.data.ethnicities)
commit('setHairsList', response.data.hairs)
commit('setEyesList', response.data.eyes)
commit('setPiercingsList', response.data.piercings)
commit('setTattoosList', response.data.tattoos)
commit('setSmokingsList', response.data.smokings)
commit('setDrinkingsList', response.data.drinkings)
commit('setStatusesList', response.data.statuses)
commit('setEducationsList', response.data.educations)
commit('setAgesList', response.data.ages)
return Promise.resolve(response)
})
}
Then I use mapped getters to access items from my stores.
computed: {
...mapGetters({
user: 'base/getUser',
fetishList: 'setup/getFetishesList',
localeData: 'setup/getLocale',
colorsList: 'setup/getColorsList',
rolesList: 'setup/getRolesList',
genderList: 'setup/getGendersList',
orientationList: 'setup/getOrientationsList',
lookingList: 'setup/getLookingsList',
seekingList: 'setup/getSeekingsList',
validation: 'base/getValidationErrors',
}),
}
All is working as expected except for my user object.
In my Vue inspector I can see that the user object is stored properly in Vuex as expected, but when I console.log(this.user) I get null and anytime I try to access a user property I get console errors.
Can anyone explain why this might be happening, I've never seen this before and have no idea what I'm looking for?
Thanks.
My guess is that your dispatch() (Vue.js actions are ALWAYS expected to be async) is not completing properly. This is how I would rewrite it with a single caveat:
Your base/setLoggedInUser Vuex action MUST return a Promise for this to work properly.
/*
export const instantiate = ({ commit, dispatch }) => {
return axios.get('/setup/fetch')
.then((response) => {
dispatch('base/setLoggedInUser', response.data.user, { root: true })
commit('setFetishesList', response.data.fetishes)
commit('setColorsList', response.data.colors)
commit('setRolesList', response.data.roles)
commit('setGendersList', response.data.genders)
commit('setOrientationsList', response.data.orientations)
commit('setLookingsList', response.data.lookings)
commit('setSeekingsList', response.data.seekings)
commit('setBodiesList', response.data.bodies)
commit('setHeightsList', response.data.heights)
commit('setEthnicitiesList', response.data.ethnicities)
commit('setHairsList', response.data.hairs)
commit('setEyesList', response.data.eyes)
commit('setPiercingsList', response.data.piercings)
commit('setTattoosList', response.data.tattoos)
commit('setSmokingsList', response.data.smokings)
commit('setDrinkingsList', response.data.drinkings)
commit('setStatusesList', response.data.statuses)
commit('setEducationsList', response.data.educations)
commit('setAgesList', response.data.ages)
return Promise.resolve(response)
})
}
*/
export const instantiate = ({ commit, dispatch }) => {
return axios.get('/setup/fetch')
.then((response) => Promise.all([
dispatch('base/setLoggedInUser', response.data.user, { root: true }),
Promise.resolve(response)
]))
.then(([dispatchResponse, response]) => {
commit('setFetishesList', response.data.fetishes)
commit('setColorsList', response.data.colors)
commit('setRolesList', response.data.roles)
commit('setGendersList', response.data.genders)
commit('setOrientationsList', response.data.orientations)
commit('setLookingsList', response.data.lookings)
commit('setSeekingsList', response.data.seekings)
commit('setBodiesList', response.data.bodies)
commit('setHeightsList', response.data.heights)
commit('setEthnicitiesList', response.data.ethnicities)
commit('setHairsList', response.data.hairs)
commit('setEyesList', response.data.eyes)
commit('setPiercingsList', response.data.piercings)
commit('setTattoosList', response.data.tattoos)
commit('setSmokingsList', response.data.smokings)
commit('setDrinkingsList', response.data.drinkings)
commit('setStatusesList', response.data.statuses)
commit('setEducationsList', response.data.educations)
commit('setAgesList', response.data.ages)
return Promise.resolve(response)
})
}
There are two main posibilities here:
The first one is that you might not be defining properly the user getter.
The second one, console.log is being executed previous to the data being set by this action:
dispatch('base/setLoggedInUser', response.data.user, { root: true })
Vuex actions are asynchronous, so setLoggedInUser could have started before the console.log (and the code giving you errors) is executed, but the actual data might not have been received yet at that point (it would be undefined).
If this is the case, add the following condition to the part of the template or the component(s) that are using the block of code where you are getting those errors:
v-if="user"
This will make Vue to wait for the mapped getter user to have a value to mount said template segment or components, avoiding trying to access properties of undefined.

Listen for changes in redis list

I want to write a function that constantly listens for changes in a redis list (usually when elements are pushed to the list) and pops its elements whenever the list is not empty. Then it will execute a function on the popped elements. How should I implement this?
You can use notify-keyspace-events for that
example with Node.js but the idea is similar for other languages.
const Redis = require('ioredis')
const redis = new Redis()
;(async function () {
redis.on('ready', () => {
console.log('ready');
redis.config('set', 'notify-keyspace-events', 'KEl')
// KEl => see https://redis.io/topics/notifications to understand the configuration
// l is meant we are interested in list event
redis.psubscribe(['__key*__:*'])
redis.on('pmessage', function(pattern, channel, message) {
console.log('got %s', message);
});
})
})()
Example output

How to populate the store and sequentially await return using Redux Observable?

I am attempting to use Redux Observable to call an action to fetch some data, wait for its return, then fetch some more data that relies on it.
I have an epic which populates a store from a fetch FetchTodos. This listens for the FETCH_TODOS action and then calls my todos API and populates {todos: [] } =
I also have a comments section in my store todoComments. However, I would like to only populate todoComments once FETCH_TODOS has returned and populated the store.
In imperative code, this might look like:
let todos = await api.get('/todos');
await dispatch("FETCH_TODO_COMPLETE", todos)
let firstId = getState().todos[0].id
let comments = await api.get(`/todos/${firstId}/comments')
await dispatch("FETCH_COMMENTS_COMPLETE", { todo_id: firstId, comments})
The closest I saw to this was this issue in the Redux Observable Repo, but I could not understand how to do this efficiently. This is a pretty common scenario for me.
I would like to reuse as much code as possible. In this example, I may dispatch FETCH_TODOS from multiple components.
How would i accomplish this with Redux-Observable?
Based on our conversation in the comments:
In redux-observable, you can sequence things in numerous ways. You could do it all in one epic using normal RxJS, or you could split them into multiple ones. If you split them, the subsequent epic would listen for the signal that the previous one has completed its task. Something like this:
// this assumes you make your `api.get` helper return an Observable
// instead of a Promise which is highly advisable.
// If it doesn't, you could do:
// Observable.from(api.get('/url'))
// but Promises are not truly cancellable which can cause max
// concurrent connections issues
const fetchTodosEpic = action$ =>
action$.ofType('FETCH_TODOS')
.switchMap(() =>
api.get('/todos')
.map(todos => ({
type: 'FETCH_TODOS_COMPLETE',
todos
}))
);
const fetchComments = action$ =>
action$.ofType('FETCH_TODOS_COMPLETE')
.switchMap(({ todos }) =>
api.get(`/todos/${todos[0].id}/comments`)
.map(comments => ({
type: 'FETCH_COMMENTS_COMPLETE',
comments
}))
);

Redux Async Test with Webpack+karma+etc

I was trying to do a async action test but failed miserable :(
the test in question is this one: https://github.com/persocon/destiny-weekly/blob/test/test/actions/index.spec.jsx
I'm getting this error message:
1) fill in GET_OPTIONS when fetching all options is done
Async Actions
undefined is not an object (evaluating 'store.dispatch(actions.getOptions()).then')
/Users/persocon/Projects/destiny-weekly/test/test.bundle.js:14669:42 <- webpack:///test/actions/index.spec.jsx:49:7
and I have no clue what that means, if this was to help I'm more confuse then ever.
UPDATE
had to implement fetch on the action itself instead of the $.get I was using but now I get a new error pointing to my action :v even it working on the browser:
1) fill in GET_OPTIONS when fetching all options is done
Async Actions
Can't find variable: fetch
/Users/persocon/Projects/destiny-weekly/test/test.bundle.js:42473:9 <- webpack:///src/app/javascript/actions/index.jsx:32:9
/Users/persocon/Projects/destiny-weekly/test/test.bundle.js:15691:23 <- webpack:///~/redux-thunk/lib/index.js:12:0
/Users/persocon/Projects/destiny-weekly/test/test.bundle.js:14669:20 <- webpack:///test/actions/index.spec.jsx:48:19
UPDATE 2
Action code:
const setOptions = (result) => {
return {
type: 'GET_OPTIONS',
options: result
}
}
const getOptions = () => {
return dispatch => {
dispatch(startLoading())
return fetch('/api/selectActivity')
.then(response => response.json())
.then( json => {
dispatch(doneLoading());
json.unshift({advisorTypeCategory: "Selecione Uma Atividade", identifier: "", disabled: "disabled"});
dispatch(setOptions(json));
}
)
}
}
Ya, the Async Action Creators Example is using 'fetch' which is a bit contrived, but should work in theory. One simple solution might be to implement it with this isomorphic fetch library. Good luck!
The solution was to implement the Isomorphic Fetch
Because as 4m1r told it was a contrived function and couldn't be found.