Build React apps with Firebase using cache-first architecture, read optimization, and cost monitoring to prevent expensive Firestore read incidents.
Build React applications with Firebase integration following strict cache-first patterns and cost optimization practices. This skill prevents expensive Firestore read incidents through mandatory caching, read tracking, and optimized real-time listeners.
This skill enforces development practices learned from a **58 million Firebase read incident**. Every Firebase integration MUST prioritize read efficiency and implement comprehensive caching.
1. **Planning Phase**
- Think through the problem and read relevant codebase files
- Write a detailed plan to `todo.md` with checkable todo items
- Get user verification before beginning implementation
- Keep every change as simple as possible, impacting minimal code
2. **Implementation Phase**
- Work through todo items, marking complete as you go
- Provide high-level explanations of changes at each step
- Avoid massive or complex changes - prioritize simplicity
- Implement Firebase reads with cache-first pattern from the start
3. **Review Phase**
- Add review section to `todo.md` summarizing changes
- Verify ReadCounter integration and cache hit rates
- Check that all Firebase operations are tracked and optimized
Every component reading Firestore data MUST implement cache-first loading:
```javascript
// Load from cache immediately for instant display
const cachedData = cacheService.getCachedData(key);
if (cachedData) {
setData(cachedData);
readCounter.recordCacheHit('collection', 'Component', cachedData.length);
} else {
readCounter.recordCacheMiss('collection', 'Component');
// Fetch from Firestore only if cache miss
}
```
ALL Firestore operations must be tracked:
```javascript
import { readCounter } from '../services/readCounter';
// Record cache hits
readCounter.recordCacheHit(collection, component, savedReads);
// Record cache misses
readCounter.recordCacheMiss(collection, component);
// Record actual Firestore reads
readCounter.recordRead(operation, collection, component, count);
```
Use listeners that only fetch new data after cached timestamps:
```javascript
const latestTimestamp = cacheService.getLatestTimestamp(key);
const unsubscribe = onSnapshot(
query(
collection(firestore, 'messages'),
where('timestamp', '>', latestTimestamp)
),
(snapshot) => {
// Handle only new data
const newData = snapshot.docs.map(doc => doc.data());
const updatedData = cacheService.appendNewData(key, newData);
setData(updatedData);
}
);
// Always clean up listeners
return () => unsubscribe();
```
Create dedicated cache services following this pattern:
```javascript
class DataCacheService {
constructor() {
this.CACHE_PREFIX = 'focal_data_';
this.CACHE_VERSION = '1.0';
this.MAX_CACHE_AGE = 7 * 24 * 60 * 60 * 1000; // 7 days
}
getCachedData(key) {
const cacheKey = `${this.CACHE_PREFIX}${key}`;
const cached = localStorage.getItem(cacheKey);
if (!cached) return null;
const { data, timestamp, version } = JSON.parse(cached);
// Validate cache age and version
if (Date.now() - timestamp > this.MAX_CACHE_AGE) return null;
if (version !== this.CACHE_VERSION) return null;
return data;
}
setCachedData(key, data) {
const cacheKey = `${this.CACHE_PREFIX}${key}`;
const cacheObject = {
data,
timestamp: Date.now(),
version: this.CACHE_VERSION
};
localStorage.setItem(cacheKey, JSON.stringify(cacheObject));
}
getLatestTimestamp(key) {
const data = this.getCachedData(key);
if (!data || data.length === 0) return null;
// Return most recent timestamp from cached data
return data[data.length - 1].timestamp;
}
appendNewData(key, newData) {
const existing = this.getCachedData(key) || [];
const updated = [...existing, ...newData];
this.setCachedData(key, updated);
return updated;
}
clearCache(key) {
localStorage.removeItem(`${this.CACHE_PREFIX}${key}`);
}
}
```
**NEVER use these patterns:**
❌ Direct Firestore queries without caching layer
```javascript
// WRONG - No caching
const docs = await getDocs(collection(firestore, 'users'));
```
❌ Real-time listeners without cache optimization
```javascript
// WRONG - Reads entire collection
onSnapshot(collection(firestore, 'messages'), callback);
```
❌ Pagination without cache management
```javascript
// WRONG - Refetches already-cached pages
const next = query(collection(firestore, 'items'), limit(20));
```
❌ Any data fetching without ReadCounter tracking
```javascript
// WRONG - No tracking
const data = await getDoc(doc(firestore, 'users', userId));
```
Before submitting code that interacts with Firestore, verify:
**Caching Implementation:**
**ReadCounter Integration:**
**Real-Time Listener Optimization:**
**Performance Standards:**
**Technology Stack:**
**Context Providers Pattern:**
**Modal Rendering:**
**File Upload Patterns:**
```bash
npm start
npm run build
npm test
npm run lint
npm run lint:fix
```
Set Firebase config via environment variables (fallback values provided for development):
1. **Simplicity First:** Every change should impact as little code as possible
2. **Cache Everything:** No Firebase reads without caching layer
3. **Track Everything:** All operations must be recorded in ReadCounter
4. **Optimize Listeners:** Only fetch incremental updates, never full collections
5. **Plan Before Coding:** Always write to `todo.md` and get approval first
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/firebase-optimized-react-development/raw