基于MobX-State-Tree实现含Expense数组的DailyExpense模型问询
Completing the Expense Model & Building DailyExpense with MobX-State-Tree
Great start on your Expense model! Let's finish implementing it and build out the DailyExpense model to manage a collection of expenses. Here's a complete, production-ready implementation with explanations:
1. Completed Expense Model
First, we'll add missing actions, a unique identifier (for easier management in collections), and a computed view to calculate the total cost of the expense item:
import { types } from 'mobx-state-tree'; /** * Expense model which holds individual item details */ export const Expense = types.model({ // Unique identifier (auto-generated if not provided) id: types.optional(types.identifier, () => Date.now().toString()), name: types.string, price: types.number, quantity: types.number, }) .views(self => ({ // Computed total: price multiplied by quantity get total() { return self.price * self.quantity; } })) .actions(self => ({ changeName(newName) { // Optional validation: ensure name isn't empty if (newName.trim()) { self.name = newName.trim(); } }, changePrice(newPrice) { // Validation: ensure price is a positive number if (typeof newPrice === 'number' && newPrice >= 0) { self.price = newPrice; } }, changeQuantity(newQuantity) { // Validation: ensure quantity is a positive integer if (Number.isInteger(newQuantity) && newQuantity > 0) { self.quantity = newQuantity; } } }));
Key additions here:
idproperty: Usestypes.identifierto ensure uniqueness, with a default generator using timestamp for convenience.- Validation in actions: Prevents invalid state (like negative prices or empty names).
totalcomputed view: Automatically calculates the total cost of the item (no need to manually track this value).
2. DailyExpense Model (Collection of Expenses)
Next, we'll build a model to manage an array of Expense items, with actions to add, remove, and update expenses, plus a computed view for the total daily cost:
/** * DailyExpense model to manage a collection of Expense items */ export const DailyExpense = types.model({ // Array of Expense items expenses: types.array(Expense), // Optional: add a date property to track which day this expense set belongs to date: types.optional(types.string, () => new Date().toISOString().split('T')[0]) }) .views(self => ({ // Computed total of all expenses for the day get dailyTotal() { return self.expenses.reduce((sum, expense) => sum + expense.total, 0); }, // Helper to find an expense by ID getExpenseById(id) { return self.expenses.find(expense => expense.id === id); } })) .actions(self => ({ // Add a new expense to the collection addExpense(expenseData) { // Create a new Expense instance from the provided data const newExpense = Expense.create(expenseData); self.expenses.push(newExpense); }, // Remove an expense by ID removeExpense(id) { self.expenses = self.expenses.filter(expense => expense.id !== id); }, // Update an existing expense (using its ID) updateExpense(id, updates) { const expense = self.getExpenseById(id); if (!expense) return; // Apply updates using the Expense model's actions if (updates.name) expense.changeName(updates.name); if (updates.price !== undefined) expense.changePrice(updates.price); if (updates.quantity !== undefined) expense.changeQuantity(updates.quantity); } }));
Key features of this model:
expensesarray: Strongly typed to only accept Expense instances.dateproperty: Optional, but useful for tracking expenses per day (defaults to today's date).dailyTotalcomputed view: Sums up all individual expense totals for the day.- CRUD actions:
addExpense,removeExpense, andupdateExpensemake it easy to manage the collection. - Helper view:
getExpenseByIdsimplifies finding specific items in the array.
3. Example Usage
Here's how you'd use these models in your application:
// Create a new DailyExpense instance const todayExpenses = DailyExpense.create(); // Add a new expense todayExpenses.addExpense({ name: "Groceries", price: 45.50, quantity: 1 }); // Update the expense todayExpenses.updateExpense(todayExpenses.expenses[0].id, { price: 47.99, quantity: 1 }); // Remove the expense todayExpenses.removeExpense(todayExpenses.expenses[0].id); // Log the daily total console.log(todayExpenses.dailyTotal); // 0 (after removal)
Key Notes
- Immutability: MobX-State-Tree handles immutable updates under the hood, so you don't need to manually copy arrays/objects.
- Type Safety: All properties are strongly typed, which helps catch bugs early (especially in TypeScript projects).
- Reactivity: Any changes to the models will automatically trigger updates in components that observe them (thanks to MobX's reactivity system).
内容的提问来源于stack exchange,提问作者Indrajeet Latthe
相关产品推荐
相关产品推荐

