组件是什么
一个返回 JSX 的函数 = React 组件。
function Welcome() {
return <h1>Hello, World!</h1>;
}
// 用:
<Welcome />
注意:组件名首字母必须大写——React 用首字母区分组件和 HTML 标签。
Props:父传子
function Welcome({ name, age }) {
return <h1>Hello, {name}, age {age}</h1>;
}
// 用:
<Welcome name="Alice" age={30} />
// ^^ string ^^ number(用 {})
Props 是只读的——子组件不能修改父传来的值。
TypeScript 类型
interface WelcomeProps {
name: string;
age?: number; // 可选(加 ?)
onGreet?: () => void;
}
function Welcome({ name, age = 18, onGreet }: WelcomeProps) {
return (
<div>
<h1>Hello, {name}</h1>
{age && <p>Age: {age}</p>}
{onGreet && <button onClick={onGreet}>Greet me</button>}
</div>
);
}
children:传 JSX 内容
interface CardProps {
title: string;
children: React.ReactNode;
}
function Card({ title, children }: CardProps) {
return (
<div className="card">
<h2>{title}</h2>
<div className="content">{children}</div>
</div>
);
}
// 用:
<Card title="Hello">
<p>这是 children!</p>
<button>按钮</button>
</Card>
children 让组件像 HTML 标签一样包内容。
组件组合
function Page() {
return (
<Layout>
<Header />
<main>
<UserCard user={alice} />
<PostList posts={posts} />
</main>
<Footer />
</Layout>
);
}
把界面拆成小组件 + 组合——这是 React 的核心思维。
解构 + 默认值
function Button({
label = 'Click',
variant = 'primary',
onClick,
disabled = false,
}: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
disabled={disabled}
>
{label}
</button>
);
}
展开 props
function MyInput(props: React.InputHTMLAttributes<HTMLInputElement>) {
return <input {...props} className={`my-input ${props.className || ''}`} />;
}
// 用:
<MyInput type="text" placeholder="Name" onChange={...} />
// 所有 props 透传给 input
Props vs State
// Props = 父给的(不可改)
function Counter({ initial }: { initial: number }) {
// State = 自己管的(可改,下篇细讲)
const [count, setCount] = useState(initial);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
Props 来自上面,State 来自自己。
单文件多组件
// UserCard.tsx
function Avatar({ src, alt }: { src: string; alt: string }) {
return <img className="avatar" src={src} alt={alt} />;
}
function NameTag({ name }: { name: string }) {
return <span className="tag">{name}</span>;
}
export function UserCard({ user }: { user: User }) {
return (
<div className="card">
<Avatar src={user.avatar} alt={user.name} />
<NameTag name={user.name} />
</div>
);
}
小组件不导出 → 文件外不可见,作为实现细节。
命名约定
组件文件 / 名字: PascalCase (UserCard.tsx)
工具函数文件: kebab-case 或 camelCase (format-date.ts)
hooks: useXxx (useAuth.ts)
context: XxxContext (ThemeContext.tsx)
坑
- 组件名必须大写——小写会被当 HTML 标签
- props 是不可变——直接
props.name = 'x'报错或无效 - children 复杂时类型用
React.ReactNode——别用any - 别在 JSX 渲染里调副作用(fetch / setTimeout)——用 useEffect(07-09 篇细讲)
下一篇:useState + 事件。