collection.add(item) - Add Item to Collection
Quick Start (30 seconds)
const todos = createCollection([
{ id: 1, text: 'Buy milk', done: false }
]);
// Add a single item
todos.add({ id: 2, text: 'Walk dog', done: false });
console.log(todos.items);
// [
// { id: 1, text: 'Buy milk', done: false },
// { id: 2, text: 'Walk dog', done: false }
// ]
// Method chaining (returns this)
todos
.add({ id: 3, text: 'Clean room', done: false })
.add({ id: 4, text: 'Cook dinner', done: false });
console.log(todos.length); // 4
// Reactive updates ✨
effect(() => {
document.getElementById('count').textContent = `${todos.length} tasks`;
});
todos.add({ id: 5, text: 'Study', done: false });
// UI auto-updates: "5 tasks"What just happened? You added items to a collection, and the UI updated automatically!
What is collection.add(item)?
add(item) is a method that appends a new item to the end of a reactive collection.
Simply put: it's a clean way to grow your collection by adding items one at a time.
Think of it as dropping a new item into a box - it goes to the bottom of the pile.
Syntax
collection.add(item)Parameters:
item(any) - The item to add. Can be any type (object, string, number, etc.)
Returns: The collection itself (for chaining)
Why Does This Exist?
Two Approaches to Adding Items to Collections
The Reactive library offers flexible ways to add items to collections, each suited to different coding preferences.
Direct Array Manipulation
When you prefer standard JavaScript array methods and want to work with familiar patterns:
const todos = createCollection([]);
// Use standard array push
todos.items.push({ text: 'Task 1' });
// Or use spread syntax
todos.items = [...todos.items, { text: 'Task 2' }];
// Multiple additions
todos.items.push({ text: 'Task 3' });
todos.items.push({ text: 'Task 4' });This approach is great when you need: ✅ Standard JavaScript array API ✅ Familiar push/spread patterns ✅ Direct access to array methods ✅ Compatibility with existing array code
When Expressive Collection Methods Fit Your Style
In scenarios where you want semantic, chainable operations that express intent clearly, add() provides a more direct approach:
const todos = createCollection([]);
// Semantic method name
todos.add({ text: 'Task 1' });
// Method chaining for multiple additions
todos
.add({ text: 'Task 2' })
.add({ text: 'Task 3' })
.add({ text: 'Task 4' });
// Clear intent: "add item to collection"This method is especially useful when:
add() Flow:
┌──────────────────┐
│ add(item) │
└────────┬─────────┘
│
▼
Appends to items
│
▼
Returns collection
│
▼
✅ Chainable operationsWhere add() shines: ✅ Semantic clarity - Method name expresses intent ✅ Chainable operations - Returns collection for fluent API ✅ Consistent with collection API - Matches remove(), update(), etc. ✅ Expressive code - Reads like natural language ✅ Clean syntax - One method for adding items
The Choice is Yours:
- Use
todos.items.push()when you prefer standard array methods - Use
todos.add()when you want semantic, chainable collection operations - Both approaches work with reactive collections
Benefits of the add approach: ✅ Clear intent - add() clearly communicates the operation ✅ Method chaining - Returns collection for fluent operations ✅ Collection-focused - Part of a consistent collection API ✅ Readable code - Expressive method names improve clarity ✅ Consistent pattern - Matches other collection methods like remove(), update()
Mental Model
Think of a collection as a container you drop items into:
Before add() After add(item)
┌─────────────┐ ┌─────────────┐
│ Items: │ │ Items: │
│ [A] │ add(B) │ [A] │
│ [B] │ ────→ │ [B] │
│ │ │ [C] ← NEW │
└─────────────┘ └─────────────┘Key Insight: Items always go to the end (like push).
How Does It Works
The complete flow when you call add():
todos.add({ text: 'New task' })
|
▼
this.items.push(item)
|
▼
Array changes
|
▼
Reactivity triggered
|
▼
Effects re-run
|
▼
UI updates
|
▼
return this (for chaining)Implementation
// From 03_dh-reactive-collections.js
add(item) {
this.items.push(item);
return this;
}Simple and effective:
- Uses native
push()to append - With Module 02,
push()is patched for reactivity - Returns
thisfor chaining
Basic Usage
Example 1: Add Todo Items
const todos = createCollection([]);
todos.add({ id: 1, text: 'Buy groceries', done: false });
todos.add({ id: 2, text: 'Call mom', done: false });
todos.add({ id: 3, text: 'Finish report', done: false });
console.log(todos.length); // 3Example 2: Method Chaining
const cart = createCollection([]);
cart
.add({ product: 'Laptop', price: 999, qty: 1 })
.add({ product: 'Mouse', price: 25, qty: 2 })
.add({ product: 'Keyboard', price: 75, qty: 1 });
console.log(cart.length); // 3Example 3: Different Data Types
// Numbers
const numbers = createCollection([]);
numbers.add(1).add(2).add(3);
// Strings
const tags = createCollection([]);
tags.add('javascript').add('react').add('vue');
// Mixed
const mixed = createCollection([]);
mixed.add(42).add('hello').add({ key: 'value' });Real-World Examples
Example 1: User Input Handler
const messages = createCollection([]);
function sendMessage(text) {
messages.add({
id: Date.now(),
text,
user: currentUser.name,
timestamp: new Date()
});
}
// User sends messages
sendMessage('Hello!');
sendMessage('How are you?');
// Reactive UI update
effect(() => {
const container = document.getElementById('messages');
container.innerHTML = messages.items.map(m => `
<div class="message">
<strong>${m.user}:</strong> ${m.text}
</div>
`).join('');
});Example 2: Auto-Generated IDs
const items = createCollection([]);
function addItem(data) {
const id = (items.last?.id || 0) + 1; // Auto-increment
items.add({
id,
...data,
createdAt: Date.now()
});
return id;
}
const id1 = addItem({ name: 'Item 1' }); // ID: 1
const id2 = addItem({ name: 'Item 2' }); // ID: 2
const id3 = addItem({ name: 'Item 3' }); // ID: 3
console.log(items.items);
// [
// { id: 1, name: 'Item 1', createdAt: ... },
// { id: 2, name: 'Item 2', createdAt: ... },
// { id: 3, name: 'Item 3', createdAt: ... }
// ]Example 3: Add with Validation
const products = createCollection([]);
function addProduct(product) {
// Validation
if (!product.name) {
throw new Error('Product name required');
}
if (product.price <= 0) {
throw new Error('Price must be positive');
}
// Add if valid
products.add({
...product,
addedAt: Date.now()
});
console.log(`Added: ${product.name}`);
}
// Usage
addProduct({ name: 'Widget', price: 10 }); // ✅ Success
addProduct({ name: 'Gadget', price: -5 }); // ❌ Error thrownExample 4: Add from API Response
const users = createCollection([]);
async function fetchUsers() {
const response = await fetch('/api/users');
const data = await response.json();
// Add each user
data.forEach(user => {
users.add(user);
});
console.log(`Loaded ${users.length} users`);
}
// Or more efficient for bulk:
async function fetchUsersBatch() {
const response = await fetch('/api/users');
const data = await response.json();
// Replace entire array (single reactivity trigger)
users.items = data;
}Example 5: Add with Duplicate Check
const tags = createCollection([]);
function addTag(tag) {
// Check for duplicates
const exists = tags.items.some(t => t === tag);
if (!exists) {
tags.add(tag);
console.log(`Added tag: ${tag}`);
} else {
console.log(`Tag already exists: ${tag}`);
}
}
addTag('javascript'); // "Added tag: javascript"
addTag('react'); // "Added tag: react"
addTag('javascript'); // "Tag already exists: javascript"
console.log(tags.items); // ['javascript', 'react']Example 6: Add to Sorted Collection
const scores = createCollection([]);
function addScore(score) {
scores.add(score);
scores.sort((a, b) => b - a); // Keep sorted descending
}
addScore(100);
addScore(250);
addScore(180);
addScore(220);
console.log(scores.items); // [250, 220, 180, 100]Example 7: Add with Max Limit
const MAX_ITEMS = 5;
const recentItems = createCollection([]);
function addRecent(item) {
recentItems.add(item);
// Remove oldest if over limit
if (recentItems.length > MAX_ITEMS) {
recentItems.shift(); // Remove first item
}
}
// Add 6 items
addRecent('A');
addRecent('B');
addRecent('C');
addRecent('D');
addRecent('E');
addRecent('F'); // 'A' is removed
console.log(recentItems.items); // ['B', 'C', 'D', 'E', 'F']Example 8: Add with Notification
const notifications = createCollection([]);
// Show toast when new notification added
effect(() => {
if (notifications.length > 0) {
const latest = notifications.last;
showToast(latest.message, latest.type);
}
});
function notify(message, type = 'info') {
notifications.add({
id: Date.now(),
message,
type,
timestamp: new Date()
});
}
notify('File saved successfully', 'success');
notify('Connection lost', 'error');Example 9: Batch Add with Loop
const numbers = createCollection([]);
// Add multiple items
for (let i = 1; i <= 10; i++) {
numbers.add(i * 10);
}
console.log(numbers.items);
// [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
// More efficient with batch
batch(() => {
for (let i = 1; i <= 100; i++) {
numbers.add(i);
}
}); // Only triggers reactivity onceExample 10: Conditional Add
const activeUsers = createCollection([]);
function updateUserStatus(user) {
const FIVE_MINUTES = 5 * 60 * 1000;
const isActive = user.lastSeen > Date.now() - FIVE_MINUTES;
if (isActive) {
activeUsers.add(user);
}
}
// Process users
users.forEach(updateUserStatus);
console.log(`${activeUsers.length} active users`);Common Patterns
Pattern 1: Add and Return Item
function addTodo(text) {
const todo = {
id: Date.now(),
text,
done: false,
createdAt: new Date()
};
todos.add(todo);
return todo; // Return the item, not collection
}
const newTodo = addTodo('Learn patterns');
console.log(newTodo.id);Pattern 2: Bulk Add Helper
function addMultiple(items) {
items.forEach(item => collection.add(item));
}
// Usage
addMultiple([
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' }
]);
// More efficient alternative
function addMultipleBatch(newItems) {
collection.items = [...collection.items, ...newItems];
}Pattern 3: Add Unique Items
function addUnique(item, keyFn = x => x) {
const exists = collection.items.some(x =>
keyFn(x) === keyFn(item)
);
if (!exists) {
collection.add(item);
return true;
}
return false;
}
// Usage
addUnique({ id: 1, name: 'John' }, x => x.id);Pattern 4: Add with Limit
function addWithLimit(item, max = 100) {
if (collection.length >= max) {
collection.remove(collection.first); // Remove oldest
}
collection.add(item);
}Pattern 5: Add and Sort
function addSorted(item, compareFn) {
collection.add(item);
collection.sort(compareFn);
}
// Usage
addSorted({ priority: 3, task: 'Medium' },
(a, b) => b.priority - a.priority
);Important Notes
1. Returns Collection, Not Item
// ❌ Wrong: add() returns collection, not item
const newTodo = todos.add({ text: 'Task' });
console.log(newTodo.text); // Error!
// ✅ Correct: access separately
const item = { text: 'Task' };
todos.add(item);
console.log(item.text); // 'Task'
// ✅ Or get from collection
todos.add({ text: 'Task' });
console.log(todos.last.text); // 'Task'2. One Item at a Time
// ❌ Wrong: only first arg used
items.add(1, 2, 3); // Only adds 1
// ✅ Correct: chain or loop
items.add(1).add(2).add(3);
[1, 2, 3].forEach(n => items.add(n));3. Stores Reference, Not Copy
const todo = { id: 1, text: 'Task', done: false };
todos.add(todo);
// Mutating original affects collection
todo.done = true;
console.log(todos.items[0].done); // true
// Clone if needed
todos.add({ ...todo }); // Shallow copy4. Don't Use in Effects
// ❌ Wrong: infinite loop!
effect(() => {
items.add('new'); // Triggers effect → adds → triggers → ...
});
// ✅ Correct: add outside
items.add('new');
effect(() => {
console.log('Count:', items.length); // Read only
});Performance Considerations
Add is O(1) - Very Fast
// Constant time operation
collection.add(item); // O(1)Bulk Operations
// Slower: N reactivity triggers
for (let i = 0; i < 1000; i++) {
items.add(i);
}
// Faster: 1 reactivity trigger
items.items = [...items.items, ...Array(1000).fill(0).map((_, i) => i)];
// Or use batch
batch(() => {
for (let i = 0; i < 1000; i++) {
items.add(i);
}
}); // Only 1 triggerWhen to Use
Use add() For:
✅ Single items - Adding one at a time
✅ Method chaining - Fluent API
✅ User input - Forms, chat, events
✅ Event logging - Activity tracking
✅ Dynamic lists - Growing collections
Don't Use For:
❌ Bulk adding - Hundreds of items (use direct assignment)
❌ Inside effects - Creates infinite loops
❌ Need return value - Returns collection, not item
Summary
What is collection.add(item)?
A method that appends a new item to the end of a reactive collection.
Why use it?
- ✅ Clean, semantic API
- ✅ Method chaining support
- ✅ Automatic reactivity
- ✅ Clear intent
- ✅ O(1) performance
Key Takeaway:
Direct push() add() method
| |
Verbose Clean API
| |
No chaining Chainable
| |
Array method Collection method ✅One-Line Rule: Use add() to append items with a clean, chainable API.
Best Practices:
- Use
add()for single items - Chain multiple calls for fluent code
- Use batch for many items
- Don't mutate items after adding
- Validate before adding
- Avoid using in effects
Remember: add() makes growing collections simple and reactive! 🎉