Загрузка…
Загрузка…
TypeScript · middle · сложность 6
import { useState, useRef, useReducer } from "react";
// --- Props: type the object, derive children intent explicitly ---
type ButtonProps = {
variant: "primary" | "ghost";
onClick: (e: React.MouseEvent) => void;
children: React.ReactNode; // opt IN to children deliberately
};
function Button({ variant, onClick, children }: ButtonProps) {
return <button data-variant={variant} onClick={onClick}>{children}</button>;
}
// --- Extend native element props without re-typing them all ---
type InputProps = React.ComponentPropsWithoutRef<"input"> & {
label: string; // your extras on top of every real <input> attribute
};
// --- Refs: element type so .current is typed; null init = read-only DOM ref
const inputRef = useRef<HTMLInputElement>(null);
inputRef.current?.focus(); // ✅ typed, null-guarded
// --- useReducer with a discriminated action union → exhaustive switch ---
type State = { count: number };
type Action = { type: "inc" } | { type: "add"; by: number };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "inc": return { count: state.count + 1 };
case "add": return { count: state.count + action.by }; // action.by is known
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });
// --- Generic component: the pattern React.FC makes ugly ---
function List<T>({ items, render }: { items: T[]; render: (item: T) => React.ReactNode }) {
return <ul>{items.map((it, i) => <li key={i}>{render(it)}</li>)}</ul>;
}ComponentPropsWithoutRef, discriminated reducer actions, generic List<T> — три паттерна вместо React.FC.