Command Palette

Search for a command to run...

Hectal
Back to the lesson: Topic 22.3 — Separating Logic from UI
React · Example 3 of 3

The testing pay-off: logic and UI tested apart

Five tests, zero mocks. Every screen state is reached by passing props, which is also how Storybook stories work. Break the rule (remove .trim()) and watch the first test fail.

Put what the component does (state, data loading, rules) in a custom hook or a plain function, and how it looks in presentational components that only take props; headless components push this furthest by shipping behaviour and accessibility with no markup at all, and both halves become easy to test and reuse.

Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.

Ready for more? This phase has 5 hands-on exercises with automatic checks.

Practice questions

Write the code in the editor, run it, then open the model answer to compare.

01

Split this component: function Clock() { const [now, setNow] = useState(new Date()); useEffect(() => { const t = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(t); }, []); return <p className="clock">{now.toLocaleTimeString()}</p>; }

02

Write a headless useDisclosure({ defaultOpen }) hook that returns open, toggle, getButtonProps() and getPanelProps() with the right ARIA.

Explain it without notes

01

Why separate logic from UI in React? What do you gain?

02

What is a headless component, and why do design systems use them?

03

How would you test a component whose logic lives in a custom hook?

04

When is splitting logic from UI not worth it?

The testing pay-off: logic and UI tested apart

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

PASS matches ignoring case and spaces
PASS empty query returns everything
PASS loading state
PASS empty state
PASS list state