I have a dashboard that needs to maintain reasonably up-to-date data. I achieve this by using rtk-query with a pollingInterval
to fetch the data from my API periodically. The server maintains a short-lived caching layer to avoid multiple clients making redundant requests to the database. I also have a redux store that I update with this data, which is used throughout the application. The data polled is an array of fairly simple objects.
However, some individual object fields can be updated from the client and, between the server cache and timing overlap of updates and polling, the updated client data can be overwritten by stale polled data. I use optimistic UI updates so this results in temporarily reverted UI state.
I’m attempting to solve this by maintaining a couple flags to indicate dirty/updating records and skip those when updating my data slice with the polled data. This works as far as keeping data in sync, but I’m running into a laggy UI and potential memory leaks with the occasional crash and I feel like I may be violating some rules that break react/redux’s functionality. I’m hoping someone with more Redux experience might be able to point to something. I can post more details, but this is where the magic happens.
Data slice reducer:
updateRegistrationData(state, action: PayloadAction<RegistrationListDto>) {
const registrationList = action.payload.registrations;
// True if the data is fresh from CRM, false if from the server cache
const updateDirty = action.payload.updateDirty;
// If registrations already set, update based on dirty/saving status
if (state.registrations.length > 0) {
// Construct dictionary for quick lookups
const registrationMap = new Map<string, RegistrationDto>();
state.registrations.forEach((registration) => {
registrationMap.set(registration.registrationId, registration);
});
// Loop through registrations, taking either the new or current record depending on flags
const updatedRegistrations = registrationList.map((newRegistration) => {
const currentRegistration = registrationMap.get(
newRegistration.registrationId
);
// If the registration is currently being saved, keep the current registration
// If the registration is dirty and the updateDirty is false, keep the current registration
if (
currentRegistration &&
(currentRegistration.isSaving ||
(!updateDirty && currentRegistration.isDirty))
) {
return currentRegistration;
} else {
return newRegistration;
}
});
state.registrations = updatedRegistrations;
// No current state to sift through, so set directly
} else {
state.registrations = registrationList;
}
},