React 组件 - REACT教程

React 组件

React 组件是构建 React 应用的基本单元,可以把组件理解为可复用的、独立的 UI 单元,每个组件封装了自己的结构、样式、逻辑和状态。

React 组件是构建应用的基石,组件可以小到一个按钮,也可以大到整个页面。

组件可以分为:函数组件类组件

从技术角度讲,React 组件就是一个返回 React 元素(通常是 JSX)的 JavaScript 函数或类。

// 最简单的组件

function Hello() {

  return <h1>Hello, World!</h1>;

}

将复杂的 UI 拆分成组件树:


App

├── Header

│   ├── Logo

│   └── Navigation

│       ├── NavItem

│       └── NavItem

├── Main

│   ├── Sidebar

│   └── Content

│       ├── Article

│       └── Article

└── Footer

函数组件

函数组件是定义组件的一种简洁方法。

函数组件是一个接受 props 并返回 React 元素的 JavaScript 函数。

基础语法:

// 方式 1:函数声明

function Welcome(props) {

  return <h1>Hello, {props.name}</h1>;

}



// 方式 2:箭头函数

const Welcome = (props) => {

  return <h1>Hello, {props.name}</h1>;

};



// 方式 3:简化写法(单行返回)

const Welcome = (props) => <h1>Hello, {props.name}</h1>;

创建一个简单的函数组件:

示例代码

import React from 'react';

// 定义一个函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

export default Welcome;

在 src/index.js 中渲染该组件:

示例代码

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Welcome from './Welcome';

const root = ReactDOM.createRoot(document.getElementById("root"));
// 渲染 Welcome 组件,并传递 name 属性
root.render(<Welcome name="World" />);

这个例子展示了一个接受 name 属性并在页面上显示 "Hello, World!" 的简单组件。


类组件

类组件使用 ES6 类语法定义,通常用于需要管理状态或使用生命周期方法的情况。

基础语法:

import React, { Component } from 'react';

class Welcome extends Component {

  render() {

    return <h1>Hello, {this.props.name}</h1>;

  }

}

创建一个类组件:

示例代码

import React, { Component } from 'react';

class Welcome extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default Welcome;

在 src/index.js 中渲染该组件:

示例代码

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Welcome from './Welcome';

const root = ReactDOM.createRoot(document.getElementById("root"));
// 渲染 Welcome 组件,并传递 name 属性
root.render(<Welcome name="World" />);

测试实例

接下来我们封装一个输出 "Hello World!" 的组件,组件名为 HelloMessage:

示例代码

function HelloMessage(props) { return <h1>Hello World!</h1>; } const element = <HelloMessage />; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( element );

实例解析:

1、我们可以使用函数定义了一个组件:

function HelloMessage(props) {

    return <h1>Hello World!</h1>;

}

你也可以使用 ES6 class 来定义一个组件:

class Welcome extends React.Component {

  render() {

    return <h1>Hello World!</h1>;

  }

}

2、const element = <HelloMessage /> 为用户自定义的组件。

注意,原生 HTML 元素名以小写字母开头,而自定义的 React 类名以大写字母开头,比如 HelloMessage 不能写成 helloMessage。除此之外还需要注意组件类只能包含一个顶层标签,否则也会报错。

如果我们需要向组件传递参数,可以使用 this.props 对象,实例如下:

示例代码

function HelloMessage(props) { return <h1>Hello {props.name}!</h1>; } const element = <HelloMessage name="ez4code"/>; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( element );

以上实例中 name 属性通过 props.name 来获取。

注意,在添加属性时, class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。


复合组件

我们可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离。

以下实例我们实现了输出网站名字和网址的组件:

示例代码

function Name(props) { return <h1>网站名称:{props.name}</h1>; } function Url(props) { return <h1>网站地址:{props.url}</h1>; } function Nickname(props) { return <h1>网站小名:{props.nickname}</h1>; } function App() { return ( <div> <Name name="易知码" /> <Url url="http://www.ez4code.com" /> <Nickname nickname="ez4code" /> </div> ); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <App /> );

实例中 App 组件使用了 Name、Url 和 Nickname 组件来输出对应的信息。


Props(属性)

Props 是组件之间传递数据的方式,类似于函数的参数。

基础用法


// 父组件传递 props

function App() {

  return (

    <div>

      <Greeting name="Alice" age={25} />

      <Greeting name="Bob" age={30} />

    </div>

  );

}

// 子组件接收 props

function Greeting(props) {

  return (

    <div>

      <h1>Hello, {props.name}!</h1>

      <p>Age: {props.age}</p>

    </div>

  );

}

Props 解构


// 推荐:直接解构

function Greeting({ name, age }) {

  return (

    <div>

      <h1>Hello, {name}!</h1>

      <p>Age: {age}</p>

    </div>

  );

}

// 带默认值的解构

function Button({ text = "Submit", variant = "primary", disabled = false }) {

  return (

    <button className={variant} disabled={disabled}>

      {text}

    </button>

  );

}

Props 的类型

Props 可以是任何 JavaScript 值:


function Demo() {

  const user = { name: "Alice", age: 25 };

  const numbers = [1, 2, 3, 4, 5];

  const handleClick = () => alert("Clicked!");

  return (

    <MyComponent

      // 字符串

      title="Hello"

      // 数字

      count={42}

      // 布尔值

      isActive={true}

      // 数组

      items={numbers}

      // 对象

      user={user}

      // 函数

      onClick={handleClick}

      // JSX

      children={<p>This is content</p>}

    />

  );

}

Children Props

特殊的 props,用于传递组件的子内容:


// 方式 1:使用 props.children

function Card(props) {

  return (

    <div className="card">

      <div className="card-body">

        {props.children}

      </div>

    </div>

  );

}

// 方式 2:解构 children

function Card({ children, title }) {

  return (

    <div className="card">

      {title && <h2>{title}</h2>}

      <div className="card-body">{children}</div>

    </div>

  );

}

// 使用

function App() {

  return (

    <Card title="My Card">

      <p>This is the card content</p>

      <button>Click me</button>

    </Card>

  );

}

Props 的不可变性

重要原则:永远不要修改 props!


// 错误:修改 props

function BadComponent(props) {

  props.name = "Changed"; // 绝对不要这样做!

  return <h1>{props.name}</h1>;

}

// 正确:将 props 视为只读

function GoodComponent({ name }) {

  const displayName = name.toUpperCase(); // 创建新值

  return <h1>{displayName}</h1>;

}


组件的组合与复用

组合模式


// 基础组件

function Avatar({ src, alt }) {

  return <img src={src} alt={alt} className="avatar" />;

}

function UserInfo({ name, email }) {

  return (

    <div>

      <h3>{name}</h3>

      <p>{email}</p>

    </div>

  );

}

// 组合成复杂组件

function UserCard({ user }) {

  return (

    <div className="user-card">

      <Avatar src={user.avatar} alt={user.name} />

      <UserInfo name={user.name} email={user.email} />

    </div>

  );

}

容器组件模式


// 容器组件:处理逻辑和状态

function Panel({ title, children, collapsible = false }) {

  const [isOpen, setIsOpen] = useState(true);

  return (

    <div className="panel">

      <div className="panel-header">

        <h2>{title}</h2>

        {collapsible && (

          <button onClick={() => setIsOpen(!isOpen)}>

            {isOpen ? "Collapse" : "Expand"}

          </button>

        )}

      </div>

      {isOpen && (

        <div className="panel-content">

          {children}

        </div>

      )}

    </div>

  );

}

// 使用

function App() {

  return (

    <Panel title="Settings" collapsible>

      <p>Your settings here...</p>

    </Panel>

  );

}

高阶组件思想(通过组合实现)


// 通用的加载状态包装

function WithLoading({ isLoading, children }) {

  if (isLoading) {

    return <div>Loading...</div>;

  }

  return children;

}

// 使用

function UserList({ users, isLoading }) {

  return (

    <WithLoading isLoading={isLoading}>

      <ul>

        {users.map(user => (

          <li key={user.id}>{user.name}</li>

        ))}

      </ul>

    </WithLoading>

  );

}


组件命名规范

命名规则

// 正确:大写字母开头(PascalCase)

function UserProfile() { }

function BlogPost() { }

function NavBar() { }

// 错误:小写字母开头

function userProfile() { } // React 会将其视为 HTML 标签

文件命名

// 推荐的文件结构

src/

├── components/

│   ├── Button.jsx          // 或 Button.js

│   ├── UserCard.jsx

│   ├── Navigation/

│   │   ├── Navigation.jsx

│   │   ├── NavItem.jsx

│   │   └── index.js        // 导出 Navigation

组件的导出与导入

默认导出

// Button.jsx

export default function Button({ text }) {

  return <button>{text}</button>;

}

// App.jsx

import Button from './Button';

命名导出

// components.jsx

export function Button({ text }) {

  return <button>{text}</button>;

}

export function Input({ value, onChange }) {

  return <input value={value} onChange={onChange} />;

}

// App.jsx

import { Button, Input } from './components';

混合导出

// Card.jsx

export default function Card({ children }) {

  return <div className="card">{children}</div>;

}

export function CardHeader({ title }) {

  return <div className="card-header">{title}</div>;

}

export function CardBody({ children }) {

  return <div className="card-body">{children}</div>;

}

// App.jsx

import Card, { CardHeader, CardBody } from './Card';

组件设计原则

1. 单一职责原则

每个组件应该只做一件事。

// 不好:组件做太多事情

function UserDashboard() {

  // 获取用户数据

  // 处理表单

  // 显示图表

  // 处理导航

  // ...

}

// 好:拆分成多个组件

function UserDashboard() {

  return (

    <div>

      <UserProfile />

      <UserStats />

      <UserActivity />

    </div>

  );

}

2. 保持组件简洁

组件代码不应该超过 200-300 行。

// 好:提取子组件

function ProductCard({ product }) {

  return (

    <div className="product-card">

      <ProductImage src={product.image} />

      <ProductInfo name={product.name} price={product.price} />

      <ProductActions productId={product.id} />

    </div>

  );

}

3. 合理使用 Props

不要传递过多的 props。

// 不好:props 过多

function User({ name, age, email, address, phone, company, role, ... }) { }

// 好:使用对象

function User({ user }) {

  const { name, age, email } = user;

  // ...

}

4. 避免深层嵌套

过深的组件嵌套会导致 props drilling 问题。

// 问题:props 需要层层传递

<App>

  <Layout user={user}>

    <Sidebar user={user}>

      <Menu user={user}>

        <MenuItem user={user} />

      </Menu>

    </Sidebar>

  </Layout>

</App>

// 解决:使用 Context 或状态管理