1. 功能組件
// Cách 1: Function declaration (khuyến khích)
function Button() {
return <button>Click me</button>;
}
// Cách 2: Arrow function
const Button = () => {
return <button>Click me</button>;
};
// Cách 3: React.FC (KHÔNG khuyến khích — implicit children, generic issues)
const Button: React.FC = () => {
return <button>Click me</button>;
};
2.道具
// Define props interface
interface ButtonProps {
label: string;
variant?: 'primary' | 'secondary' | 'danger'; // Optional with union type
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
onClick: () => void;
}
function Button({ label, variant = 'primary', size = 'md', disabled = false, onClick }: ButtonProps) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled}
onClick={onClick}
>
{label}
</button>
);
}
// Usage
<Button label="Submit" variant="primary" onClick={() => console.log('clicked')} />
<Button label="Cancel" variant="secondary" onClick={handleCancel} disabled />
3. 兒童道具
interface CardProps {
title: string;
children: React.ReactNode; // Explicit children typing
}
function Card({ title, children }: CardProps) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-body">{children}</div>
</div>
);
}
// Usage
<Card title="User Profile">
<p>Name: Duy Tran</p>
<p>Role: Developer</p>
</Card>
4. React 的 TypeScript 模式
4.1.事件類型
function Form() {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log('clicked');
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
// submit
}
};
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} onKeyDown={handleKeyDown} />
<button onClick={handleClick}>Submit</button>
</form>
);
}
4.2.通用組件
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
keyExtractor: (item: T) => string | number;
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
return (
<ul>
{items.map((item) => (
<li key={keyExtractor(item)}>{renderItem(item)}</li>
))}
</ul>
);
}
// Usage
interface User {
id: number;
name: string;
}
<List<User>
items={users}
keyExtractor={(user) => user.id}
renderItem={(user) => <span>{user.name}</span>}
/>
4.3.受歧視的工會
// Props that depend on each other
type AlertProps =
| { variant: 'success'; message: string }
| { variant: 'error'; message: string; onRetry: () => void }
| { variant: 'loading' };
function Alert(props: AlertProps) {
switch (props.variant) {
case 'success':
return <div className="alert-success">{props.message}</div>;
case 'error':
return (
<div className="alert-error">
{props.message}
<button onClick={props.onRetry}>Retry</button>
</div>
);
case 'loading':
return <div className="alert-loading">Loading...</div>;
}
}
5. 組件構成
// Compound Components pattern
interface TabsProps {
children: React.ReactNode;
defaultTab?: string;
}
function Tabs({ children, defaultTab }: TabsProps) {
const [activeTab, setActiveTab] = useState(defaultTab || '');
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
function TabList({ children }: { children: React.ReactNode }) {
return <div className="tab-list" role="tablist">{children}</div>;
}
function Tab({ value, children }: { value: string; children: React.ReactNode }) {
const { activeTab, setActiveTab } = useTabsContext();
return (
<button
role="tab"
className={activeTab === value ? 'active' : ''}
onClick={() => setActiveTab(value)}
>
{children}
</button>
);
}
function TabPanel({ value, children }: { value: string; children: React.ReactNode }) {
const { activeTab } = useTabsContext();
if (activeTab !== value) return null;
return <div role="tabpanel">{children}</div>;
}
// Attach sub-components
Tabs.List = TabList;
Tabs.Tab = Tab;
Tabs.Panel = TabPanel;
// Usage
<Tabs defaultTab="overview">
<Tabs.List>
<Tabs.Tab value="overview">Overview</Tabs.Tab>
<Tabs.Tab value="settings">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="overview">Content here</Tabs.Panel>
<Tabs.Panel value="settings">Settings here</Tabs.Panel>
</Tabs>
下一篇: 鉤子深潛 — useState、useEffect、useRef、useMemo、useCallback 和自訂掛鉤。