You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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:

  • id property: Uses types.identifier to ensure uniqueness, with a default generator using timestamp for convenience.
  • Validation in actions: Prevents invalid state (like negative prices or empty names).
  • total computed 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:

  • expenses array: Strongly typed to only accept Expense instances.
  • date property: Optional, but useful for tracking expenses per day (defaults to today's date).
  • dailyTotal computed view: Sums up all individual expense totals for the day.
  • CRUD actions: addExpense, removeExpense, and updateExpense make it easy to manage the collection.
  • Helper view: getExpenseById simplifies 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:32:14