组件是什么

一个返回 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 + 事件。