- Synchronous Hydration Barrier: Halts React tree rendering until
localStorageparsing completes to prevent UI flashing. - Global Save Loop: Uses a
useEffecthook listening to state modifications to serialize and compress thetasks,shoppingList, andbudgetSettingspayloads intolocalStorage. - Atomic Global Reset: A
RESET_ALLdispatch overwrites all lists and settings simultaneously with fallback JSON objects, forcing an instantaneous sync across all routes. - Hard Data Clear: Directly purges
localStoragekeys and executeswindow.location.reload().
- Dynamic Array Calculations: Multiplies
price × quantityinline during rendering instead of storing the calculated totals in the database. - Status Toggling (
"pending"vs"bought"): Checkboxes dispatch anUPDATE_STATUS_SHOPPING_ITEMaction mapping over the array to invert the status string. - Intelligent Threshold Warnings: Re-renders specific UI nodes based on the evaluated
spentPercentage. Hitsambertailwind classes at ≥80% usage and shifts toredalert classes + negative tracking at ≥100%. - Budget Re-allocation Form: Modifies the
budgetSettings.categoryAllocationsrecord in real-time, instantly adjusting all downstream percentage visualizers.
- Two-Way Binding Protocol: Intercepts shopping list modifications in the reducer layer. If all
ShoppingListitems matching a specifictaskIdachieve a"bought"state, the parentTaskis forcibly updated to"Done". If any linked item reverts to"pending", the parent reverts to"To do".
- ISO Date Parsing: Calculates overdue flags by safely casting string dates via
new Date(isoString)and comparing againsttoday.getTime(). - Date Overrides: An injected
customTodaystring parameter allows users to manually shift the current application's internal clock for testing different calendar events.
The central architectural decision guarantees a strict "Single Source of Truth" model via React Context and useReducer. No calculated totals or percentages are ever written to the state. The appReducer serves as the sole mutation gateway.
Persisted to Context and localStorage. Only raw user inputs and UUIDs are stored.
{
"tasks": [
{
"id": "t1",
"title": "Buy fruits for Tet",
"status": "To do",
"priority": "High",
"categoryId": 2,
"startDate": "2026-02-12T08:00:00.000Z",
"endDate": ""
}
],
"shoppingList": [
{
"id": 1,
"name": "Mandarin oranges",
"categoryId": 1,
"price": 50000,
"quantity": 2,
"status": "pending",
"taskId": "t1",
"unit": "kg",
"description": ""
}
],
"taskCategories": [],
"budgetSettings": {
"totalBudget": 5000000,
"categoryAllocations": {
"1": 1500000,
"2": 3500000
}
},
"customToday": null
}All summary data flows downward through pure selector functions (e.g., calculateBudgetMetrics, selectTasksByStatus).
Computed behaviors:
totalSpent: Derived by iterating overshoppingList, multiplyingprice × quantity, and aggregating the sum.remainingBudget:totalBudget - totalSpent.spentPercentage:(totalSpent / totalBudget) * 100.taskCompletionRate: Derived via.filter(status === "Done") / tasks.length.
Trade-off chosen: This forces an O(N) algorithmic complexity on every component render, heavily utilizing .reduce() and .filter(). This prevents state desynchronization (e.g., a total bugging out while an item is deleted) at the cost of minor CPU cycles.
- Storage Quota Bottleneck: The entire database is pushed synchronously into
localStorage, which enforces a strict ~5MB hardcap. The application cannot safely surpass roughly >10,000 raw tasks and items without crashing the write stream. - No Performance Memoization: Derived state uses pure functions inline, without explicit
useMemo()caching. Deeply rendering a 5,000-item shopping array will cause noticeable frame-rate drops when the reducer forces a state flush. - Silent Storage Catch: The write-loop
useEffectwraps thelocalStorage.setItemin an emptytry...catchblock. If the local storage quota throws an error, the application silently fails to save without alerting the user. - Cross-Browser Sync Failure: Due to the offline-first persistence model, the application operates purely in an isolated origin. Users cannot natively sync states between their mobile phone and desktop.
- Timezone Fragmentation: Date-based mechanics parse ISO strings blindly. If the user shifts their device timezone between writes, "Overdue" task filtering will drift by the timezone offset margin.