Skip to content

Commit 915033d

Browse files
chore: Adding Claude Code goodies (#159)
1 parent 57a96e9 commit 915033d

25 files changed

Lines changed: 2704 additions & 333 deletions
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
---
2+
name: workleap-react-best-practices
3+
description: React performance optimization guidelines for Single Page Applications (SPA) at Workleap. Use when writing, reviewing, or refactoring React SPA code to ensure optimal performance patterns. Triggers on tasks involving React components, state management, bundle optimization, re-render prevention, rendering performance, or JavaScript performance improvements. Covers async waterfall elimination, bundle size reduction, re-render optimization, rendering efficiency, JS micro-optimizations, and advanced React patterns. Does NOT cover server-side rendering (SSR), Next.js, or server components.
4+
metadata:
5+
version: 1.0
6+
---
7+
8+
# Workleap React Best Practices
9+
10+
Performance optimization guide for React Single Page Applications (SPA), tailored for Workleap's SPA-first architecture.
11+
12+
## When to Apply
13+
14+
Reference these guidelines when:
15+
- Writing new React components
16+
- Reviewing code for performance issues
17+
- Refactoring existing React code
18+
- Optimizing bundle size or load times
19+
- Debugging unnecessary re-renders
20+
21+
## Rule Categories by Priority
22+
23+
| Priority | Category | Impact | Reference |
24+
|----------|----------|--------|-----------|
25+
| 1 | Eliminating Waterfalls | CRITICAL | [async-rules.md](references/async-rules.md) |
26+
| 2 | Bundle Size Optimization | CRITICAL | [bundle-rules.md](references/bundle-rules.md) |
27+
| 3 | Re-render Optimization | MEDIUM | [rerender-rules.md](references/rerender-rules.md) |
28+
| 4 | Rendering Performance | MEDIUM | [rendering-rules.md](references/rendering-rules.md) |
29+
| 5 | JavaScript Performance | LOW-MEDIUM | [js-rules.md](references/js-rules.md) |
30+
| 6 | Advanced Patterns | LOW | [advanced-rules.md](references/advanced-rules.md) |
31+
32+
## Quick Reference
33+
34+
### 1. Eliminating Waterfalls (CRITICAL)
35+
36+
Waterfalls are the #1 performance killer. Each sequential await adds full network latency.
37+
38+
- **Defer Await Until Needed** - Move await into branches where actually used
39+
- **Promise.all() for Independent Operations** - Run independent async operations concurrently
40+
- **Dependency-Based Parallelization** - Use promise chaining for partial dependencies
41+
- **Prevent Waterfall Chains** - Start promises early, await late
42+
43+
Read [references/async-rules.md](references/async-rules.md) for detailed rules and code examples.
44+
45+
### 2. Bundle Size Optimization (CRITICAL)
46+
47+
Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.
48+
49+
- **Route-Based Code Splitting** - Lazy-load each route so users only download code they visit
50+
- **Avoid Barrel File Imports** - Import directly from source files, not barrel re-exports
51+
- **React.lazy for Heavy Components** - Lazy-load large components not needed on initial render
52+
- **Conditional Module Loading** - Load modules only when feature is activated
53+
- **Preload on User Intent** - Preload heavy bundles on hover/focus
54+
55+
Read [references/bundle-rules.md](references/bundle-rules.md) for detailed rules and code examples.
56+
57+
### 3. Re-render Optimization (MEDIUM)
58+
59+
Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.
60+
61+
- **Defer State Reads** - Don't subscribe to state only used in callbacks
62+
- **Extract to Memoized Components** - Enable early returns before computation
63+
- **Hoist Default Non-primitive Props** - Extract default values to constants for stable memo
64+
- **Narrow Effect Dependencies** - Use primitive dependencies in effects
65+
- **Subscribe to Derived State** - Subscribe to derived booleans, not raw values
66+
- **Calculate Derived State During Render** - Derive state during render, not effects
67+
- **Functional setState** - Use functional setState for stable callbacks
68+
- **Lazy State Initialization** - Pass function to useState for expensive values
69+
- **Skip useMemo for Simple Expressions** - Avoid memo for simple primitives
70+
- **Put Logic in Event Handlers** - Put interaction logic in event handlers, not effects
71+
- **Transitions for Non-Urgent Updates** - Use startTransition for non-urgent updates
72+
- **useRef for Transient Values** - Use refs for transient frequent values
73+
74+
Read [references/rerender-rules.md](references/rerender-rules.md) for detailed rules and code examples.
75+
76+
### 4. Rendering Performance (MEDIUM)
77+
78+
Optimizing the rendering process reduces the work the browser needs to do.
79+
80+
- **Animate SVG Wrapper** - Animate div wrapper, not SVG element
81+
- **CSS content-visibility** - Use content-visibility for long lists
82+
- **Hoist Static JSX** - Extract static JSX outside components
83+
- **Optimize SVG Precision** - Reduce SVG coordinate precision
84+
- **Activity Component (Experimental)** - Use Activity for show/hide with state preservation
85+
- **Explicit Conditional Rendering** - Use ternary, not && for conditionals
86+
- **useTransition for Loading** - Prefer useTransition over manual loading state
87+
88+
Read [references/rendering-rules.md](references/rendering-rules.md) for detailed rules and code examples.
89+
90+
### 5. JavaScript Performance (LOW-MEDIUM)
91+
92+
Micro-optimizations for hot paths that can add up to meaningful improvements.
93+
94+
- **Avoid Layout Thrashing** - Batch DOM writes, avoid interleaved reads
95+
- **Build Index Maps** - Use Map for repeated lookups
96+
- **Cache Property Access** - Cache object properties in loops
97+
- **Cache Function Results** - Cache repeated function calls in module-level Map
98+
- **Cache Storage API Calls** - Cache localStorage/sessionStorage reads
99+
- **Combine Array Iterations** - Combine multiple filter/map into one loop
100+
- **Early Length Check** - Check array length before expensive comparison
101+
- **Early Return** - Return early from functions
102+
- **Hoist RegExp** - Hoist RegExp creation outside loops
103+
- **Loop for Min/Max** - Use loop instead of sort for min/max
104+
- **Set/Map Lookups** - Use Set/Map for O(1) lookups
105+
- **toSorted() Immutability** - Use toSorted() to prevent mutation bugs
106+
107+
Read [references/js-rules.md](references/js-rules.md) for detailed rules and code examples.
108+
109+
### 6. Advanced Patterns (LOW)
110+
111+
Advanced patterns for specific cases that require careful implementation.
112+
113+
- **Event Handler Refs** - Store event handlers in refs for stable subscriptions
114+
- **Initialize Once** - Initialize app once per load, not per mount
115+
- **Stable Callback Refs** - Stable callback refs for effects without re-runs
116+
117+
Read [references/advanced-rules.md](references/advanced-rules.md) for detailed rules and code examples.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Advanced Patterns
2+
3+
**Impact:** LOW
4+
**Description:** Advanced patterns for specific cases that require careful implementation.
5+
6+
---
7+
8+
## Store Event Handlers in Refs
9+
10+
Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.
11+
12+
**Incorrect (re-subscribes on every render):**
13+
14+
```tsx
15+
function useWindowEvent(event: string, handler: (e) => void) {
16+
useEffect(() => {
17+
window.addEventListener(event, handler)
18+
return () => window.removeEventListener(event, handler)
19+
}, [event, handler])
20+
}
21+
```
22+
23+
**Correct (stable subscription):**
24+
25+
```tsx
26+
function useWindowEvent(event: string, handler: (e) => void) {
27+
const handlerRef = useRef(handler)
28+
useEffect(() => {
29+
handlerRef.current = handler
30+
}, [handler])
31+
32+
useEffect(() => {
33+
const listener = (e) => handlerRef.current(e)
34+
window.addEventListener(event, listener)
35+
return () => window.removeEventListener(event, listener)
36+
}, [event])
37+
}
38+
```
39+
40+
**Future alternative:** When `useEffectEvent` ships in a stable React release, it will simplify this pattern. Until then, the ref-based approach above is the recommended production pattern.
41+
42+
---
43+
44+
## Initialize App Once, Not Per Mount
45+
46+
Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard instead.
47+
48+
**Incorrect (runs twice in dev, re-runs on remount):**
49+
50+
```tsx
51+
function Comp() {
52+
useEffect(() => {
53+
loadFromStorage()
54+
checkAuthToken()
55+
}, [])
56+
}
57+
```
58+
59+
**Correct (once per app load):**
60+
61+
```tsx
62+
let didInit = false
63+
64+
function Comp() {
65+
useEffect(() => {
66+
if (didInit) return
67+
didInit = true
68+
loadFromStorage()
69+
checkAuthToken()
70+
}, [])
71+
}
72+
```
73+
74+
Reference: [Initializing the application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)
75+
76+
---
77+
78+
## Stable Callback Refs for Effects
79+
80+
Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.
81+
82+
**Incorrect (effect re-runs on every callback change):**
83+
84+
```tsx
85+
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
86+
const [query, setQuery] = useState('')
87+
88+
useEffect(() => {
89+
const timeout = setTimeout(() => onSearch(query), 300)
90+
return () => clearTimeout(timeout)
91+
}, [query, onSearch])
92+
}
93+
```
94+
95+
**Correct (ref-based stable callback):**
96+
97+
```tsx
98+
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
99+
const [query, setQuery] = useState('')
100+
const onSearchRef = useRef(onSearch)
101+
useEffect(() => { onSearchRef.current = onSearch }, [onSearch])
102+
103+
useEffect(() => {
104+
const timeout = setTimeout(() => onSearchRef.current(query), 300)
105+
return () => clearTimeout(timeout)
106+
}, [query])
107+
}
108+
```
109+
110+
**Future: `useEffectEvent` (experimental, not in stable React):**
111+
112+
When `useEffectEvent` ships in a stable React release, it will provide a cleaner API:
113+
114+
```tsx
115+
import { useEffectEvent } from 'react'
116+
117+
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
118+
const [query, setQuery] = useState('')
119+
const onSearchEvent = useEffectEvent(onSearch)
120+
121+
useEffect(() => {
122+
const timeout = setTimeout(() => onSearchEvent(query), 300)
123+
return () => clearTimeout(timeout)
124+
}, [query])
125+
}
126+
```

0 commit comments

Comments
 (0)