Componentes React

Los componentes React son las unidades básicas para construir aplicaciones React. Se pueden entender como unidades de interfaz de usuario (UI) reutilizables e independientes; cada componente encapsula su propia estructura, estilos, lógica y estado.

Los componentes React son los pilares para construir aplicaciones; un componente puede ser tan pequeño como un botón o tan grande como una página completa.

Los componentes se pueden dividir en:Componentes de funciónyComponentes de clase。

Desde un punto de vista técnico, un componente React es una función o clase de JavaScript que devuelve un elemento React (generalmente JSX).

// 最简单的组件
function Hello() {
  return <h1>Hello, World!</h1>;
}

Dividir una interfaz de usuario compleja en un árbol de componentes:

App
├── Header
│   ├── Logo
│   └── Navigation
│       ├── NavItem
│       └── NavItem
├── Main
│   ├── Sidebar
│   └── Content
│       ├── Article
│       └── Article
└── Footer

Componentes de función

Los componentes de función son una forma concisa de definir componentes.

Un componente de función es una función de JavaScript que recibe props y devuelve un elemento React.

Sintaxis básica:

// 方式 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>;

Cree un componente de función simple:

Archivo src/Welcome.js:

import React from 'react';

// Definir un componente de función
function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

export default Welcome;

Renderice este componente en src/index.js:

Archivo 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"));
// Renderiza el componente Welcome y pasa la propiedad name
root.render(<Welcome name="World" />);

Este ejemplo muestra un componente simple que recibe la propiedad name y muestra "Hello, World!" en la página.


Componentes de clase

Los componentes de clase se definen con la sintaxis de clases de ES6 y se usan normalmente cuando se necesita gestionar estado o utilizar métodos de ciclo de vida.

Sintaxis básica:

import React, { Component } from 'react';
class Welcome extends Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

Cree un componente de clase:

Ejemplo

import React, { Component } from 'react';

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

export default Welcome;

Renderice este componente en src/index.js:

Ejemplo

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

const root = ReactDOM.createRoot(document.getElementById("root"));
// Renderiza el componente Welcome y pasa la propiedad name
root.render(<Welcome name="World" />);

Ejemplo de prueba

A continuación, creamos un componente llamado HelloMessage que muestra "Hello World!":

Ejemplo React

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

Pruébalo »

Análisis del ejemplo:

1. Podemos definir un componente usando una función:

function HelloMessage(props) {
    return <h1>Hello World!</h1>;
}

También puede usar una clase ES6 para definir un componente:

class Welcome extends React.Component {
  render() {
    return <h1>Hello World!</h1>;
  }
}

2, const element = <HelloMessage />Es un componente definido por el usuario.

Tenga en cuenta que los nombres de los elementos HTML nativos comienzan con letra minúscula, mientras que los nombres de las clases React personalizadas comienzan con letra mayúscula; por ejemplo, HelloMessage no puede escribirse como helloMessage. Además, la clase del componente solo puede contener una etiqueta de nivel superior; de lo contrario, se producirá un error.

Si necesitamos pasar parámetros al componente, podemos usarthis.propsel objeto, como se muestra a continuación:

Ejemplo React

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

Pruébalo »

En el ejemplo anterior,nameLas propiedades seprops.nameobtienen a través de props.

Nota: al agregar atributos, el atributo class debe escribirse como className, y el atributo for debe escribirse como htmlFor, porque class y for son palabras reservadas de JavaScript.


Componentes compuestos

Podemos componer un componente creando varios componentes, es decir, separando las diferentes funcionalidades del componente.

El siguiente ejemplo implementa un componente que muestra el nombre y la URL de un sitio web:

Ejemplo React

function Name(props) { return <h1>Nombre del sitio web:{props.name}</h1>; } function Url(props) { return <h1>URL del sitio web:{props.url}</h1>; } function Nickname(props) { return <h1>Apodo del sitio web:{props.nickname}</h1>; } function App() { return ( <div> <Name name="Tutorial para novatos" /> <Url url="http://www.example.com" /> <Nickname nickname="Ejemplo" /> </div> ); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <App /> );

Pruébalo »

En el ejemplo, el componente App utiliza los componentes Name, Url y Nickname para mostrar la información correspondiente.


Props (propiedades)

Las Props son el mecanismo para pasar datos entre componentes, similar a los parámetros de una función.

Uso básico

// 父组件传递 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>
  );
}

Desestructuración de 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>
  );
}

Tipos de Props

Las Props pueden ser cualquier valor de 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 especiales, utilizadas para pasar el contenido hijo de un componente:

// 方式 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>
  );
}

Inmutabilidad de las Props

Principio importante: ¡nunca modifiques las 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>;
}

Composición y reutilización de componentes

Patrón de composición

// 基础组件
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>
  );
}

Patrón de componentes contenedores

// 容器组件:处理逻辑和状态
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>
  );
}

Idea de los componentes de orden superior (implementada mediante composición)

// 通用的加载状态包装
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>
  );
}

Convenciones de nomenclatura de componentes

Reglas de nomenclatura

// 正确:大写字母开头(PascalCase)
function UserProfile() { }
function BlogPost() { }
function NavBar() { }
// 错误:小写字母开头
function userProfile() { } // React 会将其视为 HTML 标签

Nomenclatura de archivos

// 推荐的文件结构
src/
├── components/
│   ├── Button.jsx          // o  Button.js
│   ├── UserCard.jsx
│   ├── Navigation/
│   │   ├── Navigation.jsx
│   │   ├── NavItem.jsx
│   │   └── index.js        // 导出 Navigation

Exportación e importación de componentes

Exportación por defecto

// Button.jsx
export default function Button({ text }) {
  return <button>{text}</button>;
}
// App.jsx
import Button from './Button';

Exportación con nombre

// 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';

Exportación mixta

// 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';

Principios de diseño de componentes

1. Principio de responsabilidad única

Cada componente debería hacer solo una cosa.

// 不好:组件做太多事情
function UserDashboard() {
  // 获取用户数据
  // 处理表单
  // 显示图表
  // 处理导航
  // ...
}
// 好:拆分成多个组件
function UserDashboard() {
  return (
    <div>
      <UserProfile />
      <UserStats />
      <UserActivity />
    </div>
  );
}

2. Mantenga los componentes simples

El código de un componente no debería superar las 200-300 líneas.

// 好:提取子组件
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. Uso razonable de las Props

No pase demasiadas props.

// 不好:props 过多
function User({ name, age, email, address, phone, company, role, ... }) { }
// 好:使用对象
function User({ user }) {
  const { name, age, email } = user;
  // ...
}

4. Evite la anidación profunda

Una anidación de componentes demasiado profunda puede provocar el problema de props drilling.

// 问题:props 需要层层传递
<App>
  <Layout user={user}>
    <Sidebar user={user}>
      <Menu user={user}>
        <MenuItem user={user} />
      </Menu>
    </Sidebar>
  </Layout>
</App>
// 解决:使用 Context 或状态管理
Otras extensiones