AgentSkillsCN

Refactor React

重构 React

SKILL.md

React Refactoring Specialist Skill

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.

Your Expertise

  • React component architecture
  • Custom hooks extraction
  • Performance optimization (memoization, code splitting)
  • State management patterns (Context, Zustand, Redux)
  • TypeScript best practices for React
  • Testing React components (Vitest, RTL)

Refactoring Patterns

1. Extract Custom Hook

When to apply:

  • Stateful logic repeated in multiple components
  • Complex useState/useEffect combinations
  • Data fetching logic with loading/error states

Before:

tsx
function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    setLoading(true);
    fetchUser(userId)
      .then(setUser)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [userId]);

  if (loading) return <Spinner />;
  if (error) return <Error message={error.message} />;
  return <div>{user?.name}</div>;
}

After:

tsx
// hooks/useUser.ts
function useUser(userId: string) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    setLoading(true);
    fetchUser(userId)
      .then(setUser)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [userId]);

  return { user, loading, error };
}

// components/UserProfile.tsx
function UserProfile({ userId }: { userId: string }) {
  const { user, loading, error } = useUser(userId);

  if (loading) return <Spinner />;
  if (error) return <Error message={error.message} />;
  return <div>{user?.name}</div>;
}

Checklist:

  • Hook name starts with use
  • All dependencies in useEffect array
  • Hook is testable in isolation
  • TypeScript types exported

2. Split Large Component

When to apply:

  • Component > 150 lines
  • Multiple distinct UI sections
  • Different update frequencies in sections

Strategy:

  1. Identify logical UI boundaries
  2. Extract each section as a child component
  3. Pass only necessary props (avoid prop drilling)
  4. Consider composition over props for flexibility

Before:

tsx
function Dashboard() {
  const [user, setUser] = useState<User | null>(null);
  const [stats, setStats] = useState<Stats | null>(null);
  const [notifications, setNotifications] = useState<Notification[]>([]);

  // 200+ lines of JSX with header, sidebar, main content, footer
  return (
    <div>
      <header>
        {/* 50 lines of header code */}
      </header>
      <aside>
        {/* 50 lines of sidebar code */}
      </aside>
      <main>
        {/* 80 lines of main content */}
      </main>
      <footer>
        {/* 30 lines of footer */}
      </footer>
    </div>
  );
}

After:

tsx
// components/Dashboard/index.tsx
function Dashboard() {
  return (
    <DashboardLayout>
      <DashboardHeader />
      <DashboardSidebar />
      <DashboardMain />
      <DashboardFooter />
    </DashboardLayout>
  );
}

// components/Dashboard/DashboardHeader.tsx
function DashboardHeader() {
  const { user } = useUser();
  return <header>...</header>;
}

// Each component manages its own state or uses shared context

3. Optimize with Memoization

When to apply:

  • Expensive computations in render
  • Components re-rendering unnecessarily
  • Callback props causing child re-renders

Patterns:

tsx
// useMemo for expensive computations
const sortedItems = useMemo(
  () => items.sort((a, b) => a.name.localeCompare(b.name)),
  [items]
);

// useCallback for stable function references
const handleClick = useCallback((id: string) => {
  setSelected(id);
}, []);

// React.memo for component memoization
const ExpensiveList = memo(function ExpensiveList({ items }: Props) {
  return <ul>{items.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
});

Warning signs requiring memoization:

  • Array/object props created in render
  • Functions defined inline as props
  • Child components with expensive renders
  • Lists with many items

4. Extract Context Provider

When to apply:

  • Prop drilling > 3 levels deep
  • Multiple components need same data
  • Theme/auth/user data used globally

Before (prop drilling):

tsx
<App user={user}>
  <Layout user={user}>
    <Sidebar user={user}>
      <UserMenu user={user} />
    </Sidebar>
  </Layout>
</App>

After (context):

tsx
// context/UserContext.tsx
const UserContext = createContext<UserContextValue | null>(null);

export function UserProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const value = useMemo(() => ({ user, setUser }), [user]);
  return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
}

export function useUserContext() {
  const context = useContext(UserContext);
  if (!context) throw new Error('useUserContext must be within UserProvider');
  return context;
}

// Usage
<UserProvider>
  <App />
</UserProvider>

function UserMenu() {
  const { user } = useUserContext(); // No prop drilling!
}

5. Convert Class to Function Component

When to apply:

  • Legacy class components
  • No lifecycle methods beyond useEffect equivalents
  • Simplify with hooks

Mapping:

ClassHooks
this.stateuseState
componentDidMountuseEffect(..., [])
componentDidUpdateuseEffect(..., [deps])
componentWillUnmountuseEffect(() => () => cleanup, [])
this.refsuseRef

Verification Checklist

Before completing any React refactoring:

  • All existing tests pass
  • Component renders same output (visual regression)
  • Event handlers work correctly
  • State updates behave identically
  • No new React warnings in console
  • TypeScript compiles without errors
  • No circular dependencies introduced

Common Gotchas

  1. Hooks Rules: Hooks must be called at the top level, same order every render
  2. Stale Closures: Ensure useEffect/useCallback dependencies are complete
  3. Object Identity: {} !== {} - memoize objects/arrays in dependencies
  4. Ref vs State: Use ref for values that don't trigger re-render
  5. Context Performance: Split contexts by update frequency

Related Commands

  • /refactor-analyze - Analyze component complexity
  • /golden-master - Capture component output before refactoring
  • /refactor-workflow - Execute refactoring with verification

React Refactoring Specialist - Part of CCASP Refactoring System