CodingNic

Transactions

Reuse the Entry Form for Editing

Transactions 24 min read

Reuse the Entry Form for Editing

Open:

text
client/src/pages/LedgerPage.jsx
client/src/components/TransactionList.jsx
client/src/components/EntryForm.jsx
client/src/context/BudgetContext.jsx

Do not create a second edit form. The add form already knows how to render and validate all entry fields. We only need to tell it which transaction is being edited.

In LedgerPage.jsx, keep the selected transaction in state:

javascript
const [editingTransaction, setEditingTransaction] = useState(null);

Pass it to the form and pass a cancel function back down:

jsx
<EntryForm
  editingTransaction={editingTransaction}
  onCancelEdit={() => setEditingTransaction(null)}
/>

Pass the setter into TransactionList so the list can start an edit:

jsx
<TransactionList onEdit={setEditingTransaction} />

In the transaction row, add an edit button:

jsx
<button
  type="button"
  onClick={() => onEdit(transaction)}
  aria-label="Edit entry"
>
  <Pencil size={14} />
</button>

Now the form needs to copy the selected transaction into its local input state. The component stays mounted while the selected transaction changes, so use an effect to synchronize the form:

javascript
useEffect(() => {
  if (!editingTransaction) return;

  setForm({
    date: editingTransaction.date,
    description: editingTransaction.description,
    category: editingTransaction.category ?? "",
    amount: String(editingTransaction.amount),
    type: editingTransaction.type,
  });
}, [editingTransaction]);

The dependency array tells React to run the synchronization when editingTransaction changes. Clicking a different pencil therefore loads a different record.

The submit handler now chooses between create and update:

javascript
if (editingTransaction) {
  await updateTransaction(editingTransaction.id, entry);
  onCancelEdit();
} else {
  await addTransaction(entry);
  setForm((current) => ({ ...current, description: "", amount: "" }));
}

Add updateTransaction() to BudgetContext so the context remains responsible for changing shared transaction state:

javascript
async function updateTransaction(id, entry) {
  const updated = await api.updateTransaction(id, entry);

  setTransactions((previous) =>
    previous.map((transaction) =>
      transaction.id === id ? updated : transaction
    )
  );

  return updated;
}

map() creates a new array. Only the object with the matching id is replaced; all other transactions are returned unchanged.

Make edit mode visible. A small editing entry label and a clear cancel action tell the learner which operation the form is about to perform.

Test it

  1. Click the pencil icon on an existing transaction.
  2. Confirm its values fill the form.
  3. Change the description and amount.
  4. Save.
  5. Confirm the original row changes without creating a duplicate.
  6. Start another edit and cancel it.

Checkpoint

The same form now supports both create and update. The selected transaction fills the inputs, PUT keeps its id, and shared state replaces only the edited record.