How to detect last state change in firebase? - firebase

I am using the firebase-js-sdk and the stateChanges function to detect incoming changes.
In my database I store messages. After subscribing the first time to stateChanges the observer is fired for each message, and at the moment I reload the UI after each update. Is there a way to prevent unnecessary UI updates?
this.firebaseMessagesObservable$.subscribe((message: any) => {
this.update(message);
this.refreshUi();
});

Related

Firebase Stream - Knowing when initial fetch complete?

I use a Firebase Realtime Database StreamSubscription to listen to events including onChildAdded and onChildChanged.
For example this will fetch all existing entries at dbRef and any future additions while the stream is active.
dbRef.onChildAdded.listen((event) async {
String? key = event.snapshot.key;
Map value = event.snapshot.value as Map;
// Do stuff with object...
});
However, as onChildAdded fetches ALL data and then subsequent additions, I don't know when the initial/existing data is finished fetching.
I could use dbRef.once() which would fetch the initial/existing data and I would know when that is complete, allowing me to present a loading UI. However, using onChildAdded after this would leave me fetching data twice.
How can I fetch the initial/existing data, know when that is done, so that a loading UI can be displayed appropriately and then listen to future additions?
firebaser here
If you listen to both child and value events in the Realtime Database, the value event for an update will only fire after all child events for that same have fired.
Knowing this, a common trick is to listen to both onChild... and onValue events (or the once future). You can then build/update the state in the child handlers, and commit the new state in the value handler.
The SDK automatically deduplicates listeners on the same query/reference, so having multiple listeners on the same query/ref will only read the data from the server once.

How to perform dependent async calls in redux?

Background
I am having react application which uses redux and redux-thunk.
Trying to understand how to orchestrate async calls. How to dispatch actions only if previously dispatched action (API call) was successul.
Use Case
I have EmployeesComponent. That component has two dummy presentational components
EmployeesList which basically lists employees.
AddEmployeeForm which is a modal for adding new employees.
When rendering the EmployeesComponent, within the useEffect hook, I dispatch an action to load employees. So far, so good.
When I user enters data in the AddEmployeeForm and submits the form, I want to achieve the load all employees again if the API call was successful.
I have issues understanding how to know if I am okay to dispatch the action to load all employees. Here is how my handler for submit looks like.
const handleSubmit = async (e) => {
e.preventDefault();
console.log("Handle submit add new employee..");
const employeeData = inputs;
// Validate data from the AddEmployeeForm
// in case of validation errors, abort
dispatch(addEmployee(employeeData)); // calls API to save the data
// How to await the result of the dispatched action (addEmployee) before continuing?
// If addEmployee was not successful abort
// The error data from the API call is saved in the redux store,
// so the UI gets notified and can display it properly
// If addEmployee was successful, I want to
clearForm(); // local state from the component
handleCloseForm(); // local state from the component
dispatch(loadEmployees());
};
Challenges
The current approach in handleSubmit smells to me. The business logic is kinda placed in the UI and the benefits of clearly separating the UI and business logic are lost. I have difficulties to figure out what is the approach to go and how to handle this use cases.
Another use case example:
dispatch login user
if login was successful, you get token from the backend
if login was successful, dispatch an action to load user data based on retrieved token
In Redux, async logic is normally written in the form of "thunk" functions. That lets you extract logic that needs to interact with the store outside of your React components, especially async logic that needs to do things like fetching data and dispatching the results.
You can also return a promise from a thunk and await that promise in the component after dispatching.

How can I avoid an infinite loop in useEffect in this situation

I am working on building a redux app and I need data from my database to end up in my redux store after a particular component is loaded. However, I need this to happen every time a user adds new data. Currently, I can either get the data to load initially by passing an empty dependency array to my effect, but this stops the data from being updated based on user actions (you would have to refresh the page to get the effect to run again). But if I add the data to my dependency array, this causes an infinite loop because the effect would be changing the data that is being watched in the dependency array. Below is the current flow of my code.
useEffect dispatches action from MarkerList component.
Redux action retrieves data from Firestore and dispatches action to the reducer.
Reducer updates state with payload from Firestore.
I don't know how to correctly get my effect to trigger every time a new marker is added. If I can't track my data in the dependency array due to an infinite loop, how am I supposed to monitor changes to the data and retrigger the effect?
I have tried setting up a firestore onSnapshot listener but that doesn't play nicely with redux. I have tried using an empty dependency array as stated previously which correctly fetches data the first time, but does not update my ui when new data is added without refreshing the page. Obviously, tracking the markers in the dependency array causes an infinite loop.
Effect in component
useEffect(() => {
const loadMarkers = async () => {
props.loadMarkers();
};
loadMarkers();
}, []);
Redux Action
export function loadMarkers() {
let markers = [];
return dispatch => {
getMarkersFromDB().then(snap => {
snap.forEach(doc => {
markers.push(doc.data());
});
dispatch({
type: constants.LOAD_MARKERS,
payload: markers
});
});
};
}
I think I figured it out. I was so wrapped around trying to figure out how to make the effect handle all data manipulations, I didn't think to just call loadMarkers again from the function that adds a marker to my database. The CreateMarker component is separate so I just wired it up to redux and passed the loadMarkers action to it. Now, my MarkerList still loads the initial data on mount, but whenever a user creates a new marker, the CreateMarker components also dispatches the loadMarkers action. Everything is working now.
Thanks Azundo, talking through it with you made me realize I was being shortsighted.

Do we have any lib to capture the state changes using sagas?

Basically need to build a warning modal , when user tries to move from current page/screen to another page , showing there are some saved changes .
Any implementations using redux and redux saga
Sagas are the lib for this - they watch for any action of a specified type. Navigation will take two actions: one to indicate that navigation is about to happen (which the saga will watch) and one to actually update the current page. The saga watches for actions of the first type and shows a warning dialog if the data has changed.
Ex:
function showWarning(action) {
if (/* data has been changed but not saved */) {
displayWarningDialog(action.pageToNavigateTo)
}
else {
// action that updates the page/location
completeNavigation(action.pageToNavigateTo)
}
}
function* mySaga() {
// NAVIGATE_TO_PAGE_X are the actions that get fired when a user changes pages
yield takeEvery("NAVIGATE_TO_PAGE_1", showWarning)
yield takeEvery("NAVIGATE_TO_PAGE_2", showWarning)
}
There is the amazing Redux DevTools for state debugging. This tool was built by Redux author himself.
Here are its features
Lets you inspect every state and action payload
Lets you go back in time by “cancelling” actions
If you change the reducer code, each “staged” action will be
re-evaluated
If the reducers throw, you will see during which action this
happened, and what the error was
With persistState() store enhancer, you can persist debug sessions
across page reloads
I've thought about this recently as well and been thinking about writing some form of middleware to intercept routing actions.
When intercepting a routing action, the middleware could determine if application state indicates the user is editing some unsaved data, and if so, dispatch a different action instead. That action should reduce state and cause a warning to render. The user could then confirm wanting to continue navigating by dispatching an action also intercepted by the middleware to continue the routing process.

How to know when user document loaded in Meteor Accounts

I understand that when writing code that depends on the collection being loaded into the client minimongo, that you should explicitly subscribe to the collection and pass in the appropriate callback for when it is finished loading.
My problem is that I store a lot of important subdocuments that my page needs to access in the users collection. I am using Meteor Accounts, and am trying to figure out a similar way to wait until the entire logged in user document is available. When using this to test:
console.log(Meteor.user());
the logged in case, it seems like it first registers an object with just the _id, and then sends the other fields later (I know I have to explicitly add other fields to publish from the server beyond email, etc.).
Is there a way for me to wait for the logged in user document to load completely before executing my code?
Thanks!
Deps.autorun (previously Meteor.autorun) reruns when something reactive changes, which might fit your use case:
Client js
Deps.autorun(function () {
if(Meteor.user() {
//Collection available
}
});
If you're using a subscription you can also use its callback. Have a read about it on the docs as you might have to customize it a bit, and remove the autopublish package as well as get your other collections set up to subscriptions
Server js:
Meteor.publish("userdata", function () {
//You might want to alter this depending on what you want to send down
return Meteor.users.find({}, {}});
});
Client js
Meteor.subscribe("userdata", function() {
//Collection available
});

Resources