"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.
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
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
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
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
CollapsibleFilterSectioncontainer - Consider: does the overall layout (header + filters + content + sidebar) deserve its own layout container?
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
- 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
ContractCardinmemo— does it make sense here? Why or why not?
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."