Skip to content

Latest commit

 

History

History
92 lines (61 loc) · 2.97 KB

File metadata and controls

92 lines (61 loc) · 2.97 KB

Exercise: Slay the Leshen

"Knowledge is the Witcher's most powerful weapon."

You've been given WitcherContractBoard.tsx — a monolithic ~350-line React component where Witchers browse, filter, and accept monster contracts. Everything is tangled together: fetching, filtering, state management, and rendering.

Your job: refactor it step by step using the rules from the presentation.


Task 1: Spot the Leshen (Rule 1)

If a component doesn't fit on your screen, split it.

  • Read through WitcherContractBoard.tsx
  • Identify the distinct responsibilities in the monolith
  • Draw boundaries around what should become separate components
  • List at least 5 components you would extract

Task 2: Split it properly (Rule 2)

A component should either "build things itself" or "put other components together" — not both.

  • Extract components so that each one either builds its own UI or composes other components
  • Create an orchestrator component that reads like a table of contents
  • Make sure you don't stop halfway — no mix of raw HTML and child components in the same file

Task 3: Move state to where it belongs (Rule 3)

If state doesn't belong to what the component does, move it out.

  • The "Accept Contract" confirmation state (confirmingContractId) doesn't belong in the root — move it to the component that actually uses it
  • The filter collapse states don't belong in the root — where should they live?
  • The search/filter state could live in a custom hook — extract useContractFilters

Task 4: Identify the container

Use containers when you share layout or behavior, but content differs each time.

  • The filter sections (Monster Type, Difficulty, Reward Range) all share the same collapse/expand behavior — extract a CollapsibleFilterSection container
  • Consider: does the overall layout (header + filters + content + sidebar) deserve its own layout container?

Task 5: Organize as a feature folder

Feature folders > layer folders

Create this structure:

features/contracts/
  ContractBoard.tsx           (orchestrator)
  ContractBoardLayout.tsx     (layout container)
  ContractBoardHeader.tsx
  ContractCard.tsx
  ContractList.tsx
  AcceptContractButton.tsx
  FilterPanel.tsx
  CollapsibleFilterSection.tsx
  AcceptedContractsSidebar.tsx
  SortControls.tsx
  useContractFilters.ts
  useAcceptedContracts.ts
  contracts.types.ts
  contracts.api.ts

Bonus Challenge

  • Add a new feature: "Save contract for later" (a bookmark button on each card). Notice how much easier it is to add after refactoring.
  • Wrap ContractCard in memo — does it make sense here? Why or why not?

What to deliver

A refactored feature folder with small, focused components:

  • No component exceeds ~100 lines
  • The root orchestrator is readable in under 10 seconds
  • State lives where it belongs
  • Shared behavior is extracted into containers

"People call Witchers boring and predictable. Good. That means they're reliable."