Use eventChannel to get real time data from Firestore - reactjs

I would like to sync my data in React with Firestore, each time some data is changed I would like to have an update/refresh of my data.
For fetching the data I have:
function* syncCustomers() {
try {
const response = yield call(fireBaseBackend.getCustomersAPI);
yield put(loadCustomers(response));
} catch (error) {
yield put(customerError(error));
}
}
Where my getCustomersAPI is:
getCustomersAPI = () => {
return new Promise((resolve, reject) => {
firebase.firestore().collection("customers").onSnapshot((snap) => {
let documents = [];
snap.docs.forEach(doc => {
let result = doc.data();
result.uid = doc.id;
documents.push(result);
});
resolve(documents);
});
});
};
This works to get my data, for the real time I implemented:
function* usersChannelSaga () {
try {
while (true) {
const response = yield call(fireBaseBackend.getCustomersAPI);
yield put(loadCustomers(response));
}
}
catch (error) {
yield put(customerError(error));
}
}
But I would like to use the eventChannel for this. How does this need to be implemented?
I tried:
function* usersChannelSaga () {
const channel = eventChannel(emit => fireBaseBackend.syncCustomers(emit));
try {
while (true) {
const data = yield take(channel);
yield put(loadCustomers(response));
}
}
catch (error) {
yield put(customerError(error));
}
}
Where syncCustomers is :
syncCustomers(emit){
return firebase.firestore().collection("customers").onSnapshot(emit);
}
Anyone an idea how to solve this?

Related

How To Know If Dispatch Is Success or No In Redux Saga

So i already create the createMonsterStart and it will hit my API, my API is returning response code, and I want to alert success when the response code is 00 otherwise it will alert failed, how can i achieve that? here is my code:
const onSubmitHandler = () => {
dispatch(createMonsterStart(monster))
if(dispatch success){
alert("success")
}else{
alert("error")
}
}
And here is the redux saga code:
export function* createMonsterAsync({ payload: { monster } }) {
try {
const user = yield select(getUser)
const a = yield call(createMonster, user.user.token, monster)
if (a.error) {
yield put(createMonsterFailure(a.error))
return false
}
const monsters = yield call(fetchMonsterAsync)
yield put(createMonsterSuccess(monsters))
} catch (error) {
yield put(createMonsterFailure(error))
}
}

Why is this private class member out of scope at the point where I try to call it?

I want to be able to call "setUser", but it's out of scope for some reason. This is in a MobX store that I've created. I'm sure it's something I'm doing fundamentally wrong, but I don't know what it is. Here's the code:
private setUser = (user: UserType) => {
this.userRegistry.set(user.Username, user);
}
loadUsersFromPoolGroups = () => {
this.loadingInitial = true;
try {
var congitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
const USER_POOL_ID = 'us-east-2_kWOEamV6i';
var params = {
UserPoolId: USER_POOL_ID
}
congitoidentityserviceprovider.listGroups(params, function(err, data) {
if (err) console.log(err, err.stack);
else {
data.Groups.forEach(group => {
var params = {
GroupName: group.GroupName,
UserPoolId: USER_POOL_ID
}
congitoidentityserviceprovider.listUsersInGroup(params, function(err1, data1) {
if (err1) console.log(err1, err1.stack);
else {
data1.Users.forEach((user) => {
this.setUser(user);
})
}
})
})
}
});
} catch (error) {
console.log('error loading users from pool groups', error)
}
}
I'm doing a similar thing in a different store with no issues.
private setSubmittal = (submittal: Submittal) => {
this.submittalRegistry.set(submittal.submittalId, submittal);
}
loadSubmittals = async () => {
this.loadingInitial = true;
try {
const submittals = await agent.Submittals.list();
submittals.data.forEach((submittal: Submittal) => {
this.setSubmittal(submittal);
})
this.setLoadingInitial(false);
} catch (error) {
console.log(error);
this.setLoadingInitial(false);
}
}
I expected to be able to call setUser and it won't let me.

Redux saga yield put unit test not working

I looking to doc and some samples online, but still not working. I use Sinon for unit test, and I keep getting this error, stuck on this one so long, can't figure it out.
expected { Object (##redux-saga/IO, combinator, ...) } to deeply equal { Object (##redux-saga/IO, combinator, ...) }
My action
export const loadingStatus = (response) => {
return { type: "LOADING_STATUS", response };
};
My saga
export function* mySampleSaga() {
try {
yield put(loadingStatus('loading'));
yield delay(1000);
const config = yield select(getConfig);
const requestCall = new SendingRequest(config);
const linkRequests = yield select(getLinks);
const response = yield call(
[requestService, requestCall.sample],
"2020-01-01",
"2020-12-21"
);
const result = get(response, 'entities.requests', {});
yield put(success(result));
yield put(loadingStatus('done'));
} catch (error) {
yield put(sendError(error));
yield put(loadingStatus('done'));
}
}
My test
describe('sample saga', () => {
const config = {
sample: "123"
};
const linkRequests = ['12345', '5678910'];
it('should update request status - happy path', () => {
const gen = mySampleSaga();
expect(gen.next().value).to.deep.equal(put(loadingStatus('loading'))); // This keep getting error below
});
it('If saga has error', () => {
const gen = mySampleSaga();
const error = new Error('error');
gen.next();
expect(gen.next().value).to.deep.equal(put(sendError(error)));
expect(gen.next().value).to.deep.equal(put(loadingStatus('done')));
expect(gen.next().done).to.equal(true);
});
});

Redux saga yield call not waiting with firebase?

I create a saga to update user information. But yield console.log('waiting'); not waiting util my Fibase update complete
export function* handleUpdateUserInfo(action) {
yield put(updateUserInfoPending());
const { userInfo, userID } = action;
const currentUser = yield select(state => state.user.currentUser);
const newUser = {
...currentUser,
...userInfo
};
try {
yield call(() => updateUserProfile(newUser, userID));
yield console.log('waiting');
} catch (error) {
yield put(updateUserInfoFailure(error));
}
}
Firebase
export const updateUserProfile = (newUser, userID) => {
try {
firestore
.collection('users')
.doc(userID)
.set(newUser);
} catch (error) {
throw error;
}
};
I want to ask how I fix it? and why it happen
If you return the call from updateUserProfile, you can await its result in the caller.
export const updateUserProfile = async (newUser, userID) => {
return firestore
.collection('users')
.doc(userID)
.set(newUser);
};
Then you call it with:
yield call(() => await updateUserProfile(newUser, userID));
yield console.log('waiting');

How to chain firebase observables using redux-saga?

I've recently made the move from ionic/angular2/Rxjs to React/React Native/Redux-sagas. I'm porting over my app from ionic to react native and have really enjoyed the process. One thing, however, I have really struggled with is using firebase in react native via redux sagas. I can do simple requests however I would like to chain three requests and get the return value as an JSON object. Currently I have this :
export const getTuteeUID = state => state.login.username;
function* getMemberProfiles(UID) {
try {
const memberObject = yield call(get, 'userProfile', UID);
return memberObject;
} catch (err) {
console.log(err);
}
}
function* getLatestConversation(grpID) {
try {
const opts = newOpts();
const refLatestConvo = firebase
.database()
.ref('conversations')
.child(`${grpID}`)
.orderByChild('createdAt')
.limitToLast(1);
yield call([refLatestConvo, refLatestConvo.on], 'value', opts.handler);
while (true) {
const { data } = yield take(opts);
if (data.val()) {
return data.val()[data._childKeys[0]];
}
return null;
}
} catch (err) {
console.log(err);
}
}
export function* getChatGroupObject(grpID, tuteeUID) {
try {
const groupObject = yield call(get, 'groups', grpID);
const memberProfiles = yield Object.keys(groupObject.members)
.filter(mem => mem !== tuteeUID)
.map(memUID => call(getMemberProfiles, memUID));
const latestConversation = yield call(getLatestConversation, grpID);
return { ...groupObject, memberProfiles, key: grpID, latestConversation };
} catch (err) {
console.log(err);
}
}
/**
*
* #return {Generator} []
*/
export function* fetchChatGroups() {
try {
const opts = newOpts();
const tuteeUID = yield select(getTuteeUID);
const refGrpIDs = firebase.database().ref('userProfile').child(`${tuteeUID}/groups`);
const snapGrpIDs = yield call([refGrpIDs, refGrpIDs.once], 'value', opts.handler);
if (snapGrpIDs.val()) {
const groupObject = yield Object.keys(snapGrpIDs.val()).map(grpID =>
call(getChatGroupObject, grpID, tuteeUID),
);
yield put(ChatAction.chatGroupsReceived(groupObject));
} else {
ChatAction.chatGroupsReceived([]);
}
} catch (error) {
console.log(error);
}
}
Now this works and returns the correct object, however if the latest conversation in the array changes the object won't update. How can I get this to continue updating? Another thing is if I were to put this in a while(true) loop, is there a way to unsubscribe from the observable? In rxjs this used to be super easy to do.

Resources