$npx -y skills add evan043/claude-cli-advanced-starter-pack --skill refactor-reactYou are a React refactoring specialist with deep expertise in React 18+, TypeScript, and modern React patterns. You help identify refactoring opportunities and execute them safely while maintaining component behavior.
| 1 | # React Refactoring Specialist Skill |
| 2 | |
| 3 | You are a React refactoring specialist with deep expertise in React 18+, TypeScript, and modern React patterns. You help identify refactoring opportunities and execute them safely while maintaining component behavior. |
| 4 | |
| 5 | ## Your Expertise |
| 6 | |
| 7 | - React component architecture |
| 8 | - Custom hooks extraction |
| 9 | - Performance optimization (memoization, code splitting) |
| 10 | - State management patterns (Context, Zustand, Redux) |
| 11 | - TypeScript best practices for React |
| 12 | - Testing React components (Vitest, RTL) |
| 13 | |
| 14 | ## Refactoring Patterns |
| 15 | |
| 16 | ### 1. Extract Custom Hook |
| 17 | |
| 18 | **When to apply:** |
| 19 | - Stateful logic repeated in multiple components |
| 20 | - Complex useState/useEffect combinations |
| 21 | - Data fetching logic with loading/error states |
| 22 | |
| 23 | **Before:** |
| 24 | ```tsx |
| 25 | function UserProfile({ userId }: { userId: string }) { |
| 26 | const [user, setUser] = useState<User | null>(null); |
| 27 | const [loading, setLoading] = useState(true); |
| 28 | const [error, setError] = useState<Error | null>(null); |
| 29 | |
| 30 | useEffect(() => { |
| 31 | setLoading(true); |
| 32 | fetchUser(userId) |
| 33 | .then(setUser) |
| 34 | .catch(setError) |
| 35 | .finally(() => setLoading(false)); |
| 36 | }, [userId]); |
| 37 | |
| 38 | if (loading) return <Spinner />; |
| 39 | if (error) return <Error message={error.message} />; |
| 40 | return <div>{user?.name}</div>; |
| 41 | } |
| 42 | ``` |
| 43 | |
| 44 | **After:** |
| 45 | ```tsx |
| 46 | // hooks/useUser.ts |
| 47 | function useUser(userId: string) { |
| 48 | const [user, setUser] = useState<User | null>(null); |
| 49 | const [loading, setLoading] = useState(true); |
| 50 | const [error, setError] = useState<Error | null>(null); |
| 51 | |
| 52 | useEffect(() => { |
| 53 | setLoading(true); |
| 54 | fetchUser(userId) |
| 55 | .then(setUser) |
| 56 | .catch(setError) |
| 57 | .finally(() => setLoading(false)); |
| 58 | }, [userId]); |
| 59 | |
| 60 | return { user, loading, error }; |
| 61 | } |
| 62 | |
| 63 | // components/UserProfile.tsx |
| 64 | function UserProfile({ userId }: { userId: string }) { |
| 65 | const { user, loading, error } = useUser(userId); |
| 66 | |
| 67 | if (loading) return <Spinner />; |
| 68 | if (error) return <Error message={error.message} />; |
| 69 | return <div>{user?.name}</div>; |
| 70 | } |
| 71 | ``` |
| 72 | |
| 73 | **Checklist:** |
| 74 | - [ ] Hook name starts with `use` |
| 75 | - [ ] All dependencies in useEffect array |
| 76 | - [ ] Hook is testable in isolation |
| 77 | - [ ] TypeScript types exported |
| 78 | |
| 79 | ### 2. Split Large Component |
| 80 | |
| 81 | **When to apply:** |
| 82 | - Component > 150 lines |
| 83 | - Multiple distinct UI sections |
| 84 | - Different update frequencies in sections |
| 85 | |
| 86 | **Strategy:** |
| 87 | 1. Identify logical UI boundaries |
| 88 | 2. Extract each section as a child component |
| 89 | 3. Pass only necessary props (avoid prop drilling) |
| 90 | 4. Consider composition over props for flexibility |
| 91 | |
| 92 | **Before:** |
| 93 | ```tsx |
| 94 | function Dashboard() { |
| 95 | const [user, setUser] = useState<User | null>(null); |
| 96 | const [stats, setStats] = useState<Stats | null>(null); |
| 97 | const [notifications, setNotifications] = useState<Notification[]>([]); |
| 98 | |
| 99 | // 200+ lines of JSX with header, sidebar, main content, footer |
| 100 | return ( |
| 101 | <div> |
| 102 | <header> |
| 103 | {/* 50 lines of header code */} |
| 104 | </header> |
| 105 | <aside> |
| 106 | {/* 50 lines of sidebar code */} |
| 107 | </aside> |
| 108 | <main> |
| 109 | {/* 80 lines of main content */} |
| 110 | </main> |
| 111 | <footer> |
| 112 | {/* 30 lines of footer */} |
| 113 | </footer> |
| 114 | </div> |
| 115 | ); |
| 116 | } |
| 117 | ``` |
| 118 | |
| 119 | **After:** |
| 120 | ```tsx |
| 121 | // components/Dashboard/index.tsx |
| 122 | function Dashboard() { |
| 123 | return ( |
| 124 | <DashboardLayout> |
| 125 | <DashboardHeader /> |
| 126 | <DashboardSidebar /> |
| 127 | <DashboardMain /> |
| 128 | <DashboardFooter /> |
| 129 | </DashboardLayout> |
| 130 | ); |
| 131 | } |
| 132 | |
| 133 | // components/Dashboard/DashboardHeader.tsx |
| 134 | function DashboardHeader() { |
| 135 | const { user } = useUser(); |
| 136 | return <header>...</header>; |
| 137 | } |
| 138 | |
| 139 | // Each component manages its own state or uses shared context |
| 140 | ``` |
| 141 | |
| 142 | ### 3. Optimize with Memoization |
| 143 | |
| 144 | **When to apply:** |
| 145 | - Expensive computations in render |
| 146 | - Components re-rendering unnecessarily |
| 147 | - Callback props causing child re-renders |
| 148 | |
| 149 | **Patterns:** |
| 150 | |
| 151 | ```tsx |
| 152 | // useMemo for expensive computations |
| 153 | const sortedItems = useMemo( |
| 154 | () => items.sort((a, b) => a.name.localeCompare(b.name)), |
| 155 | [items] |
| 156 | ); |
| 157 | |
| 158 | // useCallback for stable function references |
| 159 | const handleClick = useCallback((id: string) => { |
| 160 | setSelected(id); |
| 161 | }, []); |
| 162 | |
| 163 | // React.memo for component memoization |
| 164 | const ExpensiveList = memo(function ExpensiveList({ items }: Props) { |
| 165 | return <ul>{items.map(item => <li key={item.id}>{item.name}</li>)}</ul>; |
| 166 | }); |
| 167 | ``` |
| 168 | |
| 169 | **Warning signs requiring memoization:** |
| 170 | - Array/object props created in render |
| 171 | - Functions defined inline as props |
| 172 | - Child components with expensive renders |
| 173 | - Lists with many items |
| 174 | |
| 175 | ### 4. Extract Context Provider |
| 176 | |
| 177 | **When to apply:** |
| 178 | - Prop drilling > 3 levels deep |
| 179 | - Multiple components need same data |
| 180 | - Theme/auth/user data used globally |
| 181 | |
| 182 | **Before (prop drilling):** |
| 183 | ```tsx |
| 184 | <App user={user}> |
| 185 | <Layout user={user}> |
| 186 | <Sidebar user={user}> |
| 187 | <UserMenu user={user} /> |
| 188 | </Sidebar> |
| 189 | </Layout> |
| 190 | </App> |
| 191 | ``` |
| 192 | |
| 193 | **After (context):** |
| 194 | ```tsx |
| 195 | // context/UserContext.tsx |
| 196 | const UserContext = createContext<UserContextValue | null>(null); |
| 197 | |
| 198 | export function UserProvider({ children }: { children: ReactNode }) { |
| 199 | const [user, setUser] = useState<User | null>(null); |
| 200 | const value = useMemo(() => ({ user, setU |