Typorithm · hard

6. React props, children and DOM events

typescriptstable-typescriptinterviewindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

6. React props, children and DOM events

0%

Concept and contract

Build small typed components with explicit interaction contracts.

React props are ordinary object contracts supplied to a component. Use named props with precise values and callbacks, and let JSX check callers against them. A component’s return is rendered UI, while event handlers represent interactions that should be named and typed according to the operation they perform.

ReactNode describes renderable children more broadly than one JSX element. Do not require children when the component does not render them, and do not use a broad children contract as a substitute for a clear slot or callback API.

Mechanism and worked example

The example’s SaveButton receives saved and onToggle.

The example’s SaveButton receives saved and onToggle. Its click handler calls the callback with the next boolean and exposes the state through aria-pressed. The component does not own the persistent saved state; that belongs to its caller.

For extracted DOM handlers, use the appropriate React event type and currentTarget for the element the handler is attached to. target may identify a nested element. Inline JSX handlers often receive useful contextual types without an explicit annotation on every parameter.

Complete worked example

typescript

import type { ReactNode } from "react";
type Props = { saved: boolean; onToggle: (next: boolean) => void; children?: ReactNode };
export function SaveButton({saved,onToggle,children}: Props) {
  return <button type="button" aria-pressed={saved} onClick={()=>onToggle(!saved)}>{children ?? "Save role"}</button>;
}
Reasoning
Props connect the displayed state to a boolean callback; the parent remains the state owner.

Counterexamples and common mistakes

The rejected props use a numeric saved value.

The rejected props use a numeric saved value. Casting the props to any would remove the caller protection and could make aria state or toggle behavior misleading. Prefer a small concrete prop contract over a large generic component until there is a real reusable relationship.

Do not confuse a typed event with a validated form value. An input’s value is text even when the input type is number. Parsing, bounds and empty-state behavior remain runtime responsibilities. Accessibility semantics also need review beyond satisfying JSX types.

Rejected counterexample: expected compiler diagnostic

typescript

type Props = { saved: boolean; onToggle: (next:boolean)=>void };
// @ts-expect-error saved is a boolean state.
const invalid: Props = {saved:1,onToggle:()=>{}};
Reasoning
A mismatched prop is rejected before rendering, but runtime callers still need valid data.

Independent practice

Implement a SearchInput receiving a string value and an onQuery callback.

Implement a SearchInput receiving a string value and an onQuery callback. Render a labeled controlled input and call onQuery with currentTarget.value on change. Begin with the value prop, then add the callback and accessible label.

Check a valid caller and a rejected number callback. Optional extension: support a disabled state with a default. The basic exercise should remain one small component, not a full design-system abstraction with polymorphic element types.

Step 1: Render a labeled controlled input. Step 2: Forward currentTarget.value through onQuery.

Practice starter: solve before reading the solution

typescript

type Props = { value: string; onQuery: (query:string)=>void };
export function SearchInput(props: Props) { return <p>TODO: controlled labeled input</p>; }
Reasoning
Build a concrete controlled input before adding optional design-system features.
  • Hint 1: The parent owns the current query value.
  • Hint 2: Use the actual event attachment element.

Solution, tests and trade-offs

The solution keeps ownership in the parent and forwards the actual input text through a string callback.

The solution keeps ownership in the parent and forwards the actual input text through a string callback. It uses currentTarget and a visible label. Type checking verifies the prop relationship, but a browser interaction test must still confirm the event fires and the label identifies the input.

Render the component in a test harness, type text and assert the callback receives it. Compile a wrong caller separately. These checks distinguish static prop correctness from keyboard behavior, focus and actual rendered accessibility.

Practice solution and acceptance checks

typescript

type Props = { value: string; onQuery: (query:string)=>void };
export function SearchInput({value,onQuery}: Props) {
  return <label>Search roles<input value={value} onChange={event=>onQuery(event.currentTarget.value)} /></label>;
}
Reasoning
The label and currentTarget-based callback make the interaction contract explicit.

Interview reasoning and next step

In interviews, explain controlled ownership, callback parameter types and ReactNode versus a single element.

In interviews, explain controlled ownership, callback parameter types and ReactNode versus a single element. Describe currentTarget versus target using a nested DOM example. Avoid saying that TypeScript automatically validates user-entered values or accessibility.

Next, add hooks and reducer state. Keeping the component contract small lets a learner understand each new state operation without carrying unrelated generic complexity into every example.

Optional stretch — after the core task works: Add a disabled prop and a keyboard interaction test.

Build small typed components with explicit interaction contracts.