diff --git a/lib/reducers/entry-reducer.js b/lib/reducers/entry-reducer.js
--- a/lib/reducers/entry-reducer.js
+++ b/lib/reducers/entry-reducer.js
@@ -119,15 +119,15 @@
   )(oldDaysToEntries);
 }
 
-function mergeNewEntryInfos(
+function mergeNewEntryInfosOps(
   currentEntryInfos: { +[id: string]: RawEntryInfo },
   currentDaysToEntries: ?{ +[day: string]: string[] },
   newEntryInfos: $ReadOnlyArray<RawEntryInfo>,
   threadInfos: RawThreadInfos,
-) {
+): $ReadOnlyArray<EntryStoreOperation> {
   const mergedEntryInfos: { [string]: RawEntryInfo } = {};
-  let someEntryUpdated = false;
 
+  const ops: Array<EntryStoreOperation> = [];
   for (const rawEntryInfo of newEntryInfos) {
     const serverID = rawEntryInfo.id;
     invariant(serverID, 'new entryInfos should have serverID');
@@ -165,8 +165,14 @@
     if (_isEqual(currentEntryInfo)(newEntryInfo)) {
       mergedEntryInfos[serverID] = currentEntryInfo;
     } else {
+      ops.push({
+        type: 'replace_entry',
+        payload: {
+          id: serverID,
+          entry: newEntryInfo,
+        },
+      });
       mergedEntryInfos[serverID] = newEntryInfo;
-      someEntryUpdated = true;
     }
   }
 
@@ -177,20 +183,19 @@
     }
   }
 
-  for (const id in mergedEntryInfos) {
-    const entryInfo = mergedEntryInfos[id];
-    if (!threadInFilterList(threadInfos[entryInfo.threadID])) {
-      someEntryUpdated = true;
-      delete mergedEntryInfos[id];
-    }
+  const entriesFromOutsideFilterList = Object.entries(mergedEntryInfos)
+    .filter(([, entry]) => !threadInFilterList(threadInfos[entry.threadID]))
+    .map(([id]) => id);
+  if (entriesFromOutsideFilterList.length > 0) {
+    ops.push({
+      type: 'remove_entries',
+      payload: {
+        ids: entriesFromOutsideFilterList,
+      },
+    });
   }
 
-  const daysToEntries =
-    !currentDaysToEntries || someEntryUpdated
-      ? daysToEntriesFromEntryInfos(values(mergedEntryInfos))
-      : currentDaysToEntries;
-  const entryInfos = someEntryUpdated ? mergedEntryInfos : currentEntryInfos;
-  return [entryInfos, daysToEntries];
+  return ops;
 }
 
 function reduceEntryInfos(
@@ -280,19 +285,15 @@
       reportCreationRequests: [],
     };
   } else if (action.type === fetchEntriesActionTypes.success) {
-    const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos(
+    const ops = mergeNewEntryInfosOps(
       entryInfos,
       daysToEntries,
       action.payload.rawEntryInfos,
       newThreadInfos,
     );
     return {
-      entryStore: {
-        entryInfos: updatedEntryInfos,
-        daysToEntries: updatedDaysToEntries,
-        lastUserInteractionCalendar,
-      },
-      entryStoreOperations: [],
+      entryStore: entryStoreOpsHandlers.processStoreOperations(entryStore, ops),
+      entryStoreOperations: ops,
       reportCreationRequests: [],
     };
   } else if (
@@ -313,23 +314,27 @@
     const newLastUserInteractionCalendar = action.payload.calendarQuery
       ? Date.now()
       : lastUserInteractionCalendar;
-    const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos(
+    const mergeEntriesOps = mergeNewEntryInfosOps(
       entryInfos,
       daysToEntries,
       action.payload.rawEntryInfos,
       newThreadInfos,
     );
-    const deletionMarkedEntryInfos = markDeletedEntries(
+    const updatedEntryInfos = entryStoreOpsHandlers.processStoreOperations(
+      entryStore,
+      mergeEntriesOps,
+    ).entryInfos;
+    const markAsDeletedOps = markDeletedEntries(
       updatedEntryInfos,
       action.payload.deletedEntryIDs,
     );
+    const ops = [...mergeEntriesOps, ...markAsDeletedOps];
     return {
       entryStore: {
-        entryInfos: deletionMarkedEntryInfos,
-        daysToEntries: updatedDaysToEntries,
+        ...entryStoreOpsHandlers.processStoreOperations(entryStore, ops),
         lastUserInteractionCalendar: newLastUserInteractionCalendar,
       },
-      entryStoreOperations: [],
+      entryStoreOperations: ops,
       reportCreationRequests: [],
     };
   } else if (action.type === createLocalEntryActionType) {
@@ -385,7 +390,7 @@
       };
     }
 
-    const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos(
+    const ops = mergeNewEntryInfosOps(
       rekeyedEntryInfos,
       null,
       mergeUpdateEntryInfos([], action.payload.updatesResult.viewerUpdates),
@@ -393,11 +398,10 @@
     );
     return {
       entryStore: {
-        entryInfos: updatedEntryInfos,
-        daysToEntries: updatedDaysToEntries,
+        ...entryStoreOpsHandlers.processStoreOperations(entryStore, ops),
         lastUserInteractionCalendar: Date.now(),
       },
-      entryStoreOperations: [],
+      entryStoreOperations: ops,
       reportCreationRequests: [],
     };
   } else if (action.type === saveEntryActionTypes.success) {
@@ -414,7 +418,7 @@
       };
     }
 
-    const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos(
+    const ops = mergeNewEntryInfosOps(
       entryInfos,
       daysToEntries,
       mergeUpdateEntryInfos([], action.payload.updatesResult.viewerUpdates),
@@ -423,11 +427,10 @@
 
     return {
       entryStore: {
-        entryInfos: updatedEntryInfos,
-        daysToEntries: updatedDaysToEntries,
+        ...entryStoreOpsHandlers.processStoreOperations(entryStore, ops),
         lastUserInteractionCalendar: Date.now(),
       },
-      entryStoreOperations: [],
+      entryStoreOperations: ops,
       reportCreationRequests: [],
     };
   } else if (action.type === concurrentModificationResetActionType) {
@@ -485,19 +488,18 @@
   } else if (action.type === deleteEntryActionTypes.success) {
     const { payload } = action;
     if (payload) {
-      const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos(
+      const ops = mergeNewEntryInfosOps(
         entryInfos,
         daysToEntries,
         mergeUpdateEntryInfos([], payload.updatesResult.viewerUpdates),
         newThreadInfos,
       );
       return {
-        entryStore: {
-          entryInfos: updatedEntryInfos,
-          daysToEntries: updatedDaysToEntries,
-          lastUserInteractionCalendar,
-        },
-        entryStoreOperations: [],
+        entryStore: entryStoreOpsHandlers.processStoreOperations(
+          entryStore,
+          ops,
+        ),
+        entryStoreOperations: ops,
         reportCreationRequests: [],
       };
     }
@@ -533,7 +535,7 @@
       reportCreationRequests: [],
     };
   } else if (action.type === restoreEntryActionTypes.success) {
-    const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos(
+    const ops = mergeNewEntryInfosOps(
       entryInfos,
       daysToEntries,
       mergeUpdateEntryInfos([], action.payload.updatesResult.viewerUpdates),
@@ -541,11 +543,10 @@
     );
     return {
       entryStore: {
-        entryInfos: updatedEntryInfos,
-        daysToEntries: updatedDaysToEntries,
+        ...entryStoreOpsHandlers.processStoreOperations(entryStore, ops),
         lastUserInteractionCalendar: Date.now(),
       },
-      entryStoreOperations: [],
+      entryStoreOperations: ops,
       reportCreationRequests: [],
     };
   } else if (
@@ -555,24 +556,23 @@
   ) {
     const { calendarResult } = action.payload;
     if (calendarResult) {
-      const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos(
+      const ops = mergeNewEntryInfosOps(
         entryInfos,
         daysToEntries,
         calendarResult.rawEntryInfos,
         newThreadInfos,
       );
       return {
-        entryStore: {
-          entryInfos: updatedEntryInfos,
-          daysToEntries: updatedDaysToEntries,
-          lastUserInteractionCalendar,
-        },
-        entryStoreOperations: [],
+        entryStore: entryStoreOpsHandlers.processStoreOperations(
+          entryStore,
+          ops,
+        ),
+        entryStoreOperations: ops,
         reportCreationRequests: [],
       };
     }
   } else if (action.type === incrementalStateSyncActionType) {
-    const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos(
+    const mergeEntriesOps = mergeNewEntryInfosOps(
       entryInfos,
       daysToEntries,
       mergeUpdateEntryInfos(
@@ -581,17 +581,18 @@
       ),
       newThreadInfos,
     );
-    const deletionMarkedEntryInfos = markDeletedEntries(
+    const updatedEntryInfos = entryStoreOpsHandlers.processStoreOperations(
+      entryStore,
+      mergeEntriesOps,
+    ).entryInfos;
+    const markAsDeletedOps = markDeletedEntries(
       updatedEntryInfos,
       action.payload.deletedEntryIDs,
     );
+    const ops = [...mergeEntriesOps, ...markAsDeletedOps];
     return {
-      entryStore: {
-        entryInfos: deletionMarkedEntryInfos,
-        daysToEntries: updatedDaysToEntries,
-        lastUserInteractionCalendar,
-      },
-      entryStoreOperations: [],
+      entryStore: entryStoreOpsHandlers.processStoreOperations(entryStore, ops),
+      entryStoreOperations: ops,
       reportCreationRequests: [],
     };
   } else if (
@@ -599,35 +600,27 @@
     action.type === joinThreadActionTypes.success ||
     action.type === newThreadActionTypes.success
   ) {
-    const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos(
+    const ops = mergeNewEntryInfosOps(
       entryInfos,
       daysToEntries,
       mergeUpdateEntryInfos([], action.payload.updatesResult.newUpdates),
       newThreadInfos,
     );
     return {
-      entryStore: {
-        entryInfos: updatedEntryInfos,
-        daysToEntries: updatedDaysToEntries,
-        lastUserInteractionCalendar,
-      },
-      entryStoreOperations: [],
+      entryStore: entryStoreOpsHandlers.processStoreOperations(entryStore, ops),
+      entryStoreOperations: ops,
       reportCreationRequests: [],
     };
   } else if (action.type === fullStateSyncActionType) {
-    const [updatedEntryInfos, updatedDaysToEntries] = mergeNewEntryInfos(
+    const ops = mergeNewEntryInfosOps(
       entryInfos,
       daysToEntries,
       action.payload.rawEntryInfos,
       newThreadInfos,
     );
     return {
-      entryStore: {
-        entryInfos: updatedEntryInfos,
-        daysToEntries: updatedDaysToEntries,
-        lastUserInteractionCalendar,
-      },
-      entryStoreOperations: [],
+      entryStore: entryStoreOpsHandlers.processStoreOperations(entryStore, ops),
+      entryStoreOperations: ops,
       reportCreationRequests: [],
     };
   } else if (
@@ -679,42 +672,45 @@
       };
     }
 
-    const updatedEntryInfos: { [string]: RawEntryInfo } = { ...entryInfos };
+    const ops: Array<EntryStoreOperation> = [];
+    let updatedEntryInfos = entryInfos;
     if (deleteEntryIDs) {
-      for (const deleteEntryID of deleteEntryIDs) {
-        delete updatedEntryInfos[deleteEntryID];
-      }
+      ops.push({
+        type: 'remove_entries',
+        payload: {
+          ids: deleteEntryIDs,
+        },
+      });
+      updatedEntryInfos = entryStoreOpsHandlers.processStoreOperations(
+        entryStore,
+        ops,
+      ).entryInfos;
     }
 
-    let mergedEntryInfos: { +[string]: RawEntryInfo };
-    let mergedDaysToEntries;
     if (rawEntryInfos) {
-      [mergedEntryInfos, mergedDaysToEntries] = mergeNewEntryInfos(
-        updatedEntryInfos,
-        null,
-        rawEntryInfos,
-        newThreadInfos,
-      );
-    } else {
-      mergedEntryInfos = updatedEntryInfos;
-      mergedDaysToEntries = daysToEntriesFromEntryInfos(
-        values(updatedEntryInfos),
+      ops.push(
+        ...mergeNewEntryInfosOps(
+          updatedEntryInfos,
+          null,
+          rawEntryInfos,
+          newThreadInfos,
+        ),
       );
     }
 
+    const newStore = entryStoreOpsHandlers.processStoreOperations(
+      entryStore,
+      ops,
+    );
     const newInconsistencies = stateSyncSpecs.entries.findStoreInconsistencies(
       action,
       entryInfos,
-      mergedEntryInfos,
+      newStore.entryInfos,
     );
 
     return {
-      entryStore: {
-        entryInfos: mergedEntryInfos,
-        daysToEntries: mergedDaysToEntries,
-        lastUserInteractionCalendar,
-      },
-      entryStoreOperations: [],
+      entryStore: newStore,
+      entryStoreOperations: ops,
       reportCreationRequests: newInconsistencies,
     };
   } else if (action.type === setClientDBStoreActionType) {
@@ -766,22 +762,25 @@
 function markDeletedEntries(
   entryInfos: { +[id: string]: RawEntryInfo },
   deletedEntryIDs: $ReadOnlyArray<string>,
-): { +[id: string]: RawEntryInfo } {
-  let result = entryInfos;
+): $ReadOnlyArray<EntryStoreOperation> {
+  const ops = [];
   for (const deletedEntryID of deletedEntryIDs) {
     const entryInfo = entryInfos[deletedEntryID];
     if (!entryInfo || entryInfo.deleted) {
       continue;
     }
-    result = {
-      ...result,
-      [deletedEntryID]: {
-        ...entryInfo,
-        deleted: true,
+    ops.push({
+      type: 'replace_entry',
+      payload: {
+        id: deletedEntryID,
+        entry: {
+          ...entryInfo,
+          deleted: true,
+        },
       },
-    };
+    });
   }
-  return result;
+  return ops;
 }
 
 export { daysToEntriesFromEntryInfos, reduceEntryInfos };